@hiveai/mcp 0.9.0 → 0.9.3
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/README.md +13 -8
- package/dist/index.js +121 -34
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +51 -8
- package/dist/server.js +130 -21
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,11 +8,15 @@ Connect your AI coding tools to a shared, version-controlled knowledge base. Eve
|
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
|
+
**Recommended:** install only `@hiveai/cli`. The MCP server is **bundled** inside `haive` — configure clients with `command: "haive"` and `args: ["mcp", "--stdio"]` (see `@hiveai/cli` README).
|
|
12
|
+
|
|
13
|
+
Standalone package (legacy / advanced):
|
|
14
|
+
|
|
11
15
|
```bash
|
|
12
16
|
npm install -g @hiveai/mcp
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
You usually still want the CLI for `haive init`, `haive sync`, etc.:
|
|
16
20
|
|
|
17
21
|
```bash
|
|
18
22
|
npm install -g @hiveai/cli
|
|
@@ -50,8 +54,8 @@ Add to `~/.claude.json` (global) or `.claude/settings.json` (per-project):
|
|
|
50
54
|
{
|
|
51
55
|
"mcpServers": {
|
|
52
56
|
"haive": {
|
|
53
|
-
"command": "haive
|
|
54
|
-
"args": ["--root", "/absolute/path/to/your/project"]
|
|
57
|
+
"command": "haive",
|
|
58
|
+
"args": ["mcp", "--stdio", "--root", "/absolute/path/to/your/project"]
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
}
|
|
@@ -65,8 +69,8 @@ Add to `~/.cursor/mcp.json`:
|
|
|
65
69
|
{
|
|
66
70
|
"mcpServers": {
|
|
67
71
|
"haive": {
|
|
68
|
-
"command": "haive
|
|
69
|
-
"args": ["--root", "/absolute/path/to/your/project"]
|
|
72
|
+
"command": "haive",
|
|
73
|
+
"args": ["mcp", "--stdio", "--root", "/absolute/path/to/your/project"]
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -75,7 +79,7 @@ Add to `~/.cursor/mcp.json`:
|
|
|
75
79
|
### VS Code
|
|
76
80
|
|
|
77
81
|
```bash
|
|
78
|
-
code --add-mcp '{"name":"haive","command":"haive
|
|
82
|
+
code --add-mcp '{"name":"haive","command":"haive","args":["mcp","--stdio","--root","/absolute/path/to/project"]}'
|
|
79
83
|
```
|
|
80
84
|
|
|
81
85
|
### Project-scoped (auto-detected)
|
|
@@ -86,8 +90,9 @@ Add a `.mcp.json` at the project root:
|
|
|
86
90
|
{
|
|
87
91
|
"mcpServers": {
|
|
88
92
|
"haive": {
|
|
89
|
-
"command": "haive
|
|
90
|
-
"args": ["
|
|
93
|
+
"command": "haive",
|
|
94
|
+
"args": ["mcp", "--stdio"],
|
|
95
|
+
"env": { "HAIVE_PROJECT_ROOT": "/absolute/path/to/your/project" }
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/index.ts
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
|
|
6
3
|
// src/server.ts
|
|
7
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
6
|
|
|
9
7
|
// src/context.ts
|
|
10
8
|
import { findProjectRoot, resolveHaivePaths } from "@hiveai/core";
|
|
@@ -142,7 +140,9 @@ var MemSaveInputSchema = {
|
|
|
142
140
|
),
|
|
143
141
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
144
142
|
body: z4.string().describe("Markdown body of the memory"),
|
|
145
|
-
scope: z4.enum(["personal", "team", "module"]).
|
|
143
|
+
scope: z4.enum(["personal", "team", "module"]).optional().describe(
|
|
144
|
+
"Visibility scope: personal | team | module. When omitted, falls back to defaultScope in haive.config.json (default: personal)."
|
|
145
|
+
),
|
|
146
146
|
module: z4.string().optional().describe("Module name (required when scope=module)"),
|
|
147
147
|
tags: z4.array(z4.string()).default([]).describe("Tags for filtering"),
|
|
148
148
|
domain: z4.string().optional().describe("Domain (e.g. transactions, billing)"),
|
|
@@ -157,6 +157,30 @@ var MemSaveInputSchema = {
|
|
|
157
157
|
function bodyHash(body) {
|
|
158
158
|
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
159
159
|
}
|
|
160
|
+
var WORD_RE = /\b[a-z0-9]{3,}\b/gi;
|
|
161
|
+
function bodyTokenSet(body) {
|
|
162
|
+
const raw = body.toLowerCase().match(WORD_RE) ?? [];
|
|
163
|
+
return new Set(raw);
|
|
164
|
+
}
|
|
165
|
+
function maxBodySimilarity(incomingTokens, memories, scope, type, excludeIds) {
|
|
166
|
+
if (incomingTokens.size < 6) return null;
|
|
167
|
+
let best = null;
|
|
168
|
+
const skip = excludeIds ?? /* @__PURE__ */ new Set();
|
|
169
|
+
for (const { memory } of memories) {
|
|
170
|
+
const fm = memory.frontmatter;
|
|
171
|
+
if (skip.has(fm.id)) continue;
|
|
172
|
+
if (fm.scope !== scope || fm.type !== type) continue;
|
|
173
|
+
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
174
|
+
const other = bodyTokenSet(memory.body);
|
|
175
|
+
if (other.size === 0) continue;
|
|
176
|
+
let inter = 0;
|
|
177
|
+
for (const t of incomingTokens) if (other.has(t)) inter++;
|
|
178
|
+
const uni = incomingTokens.size + other.size - inter;
|
|
179
|
+
const j = uni === 0 ? 0 : inter / uni;
|
|
180
|
+
if (j >= 0.72 && (!best || j > best.score)) best = { score: j, id: fm.id };
|
|
181
|
+
}
|
|
182
|
+
return best;
|
|
183
|
+
}
|
|
160
184
|
async function memSave(input, ctx) {
|
|
161
185
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
162
186
|
throw new Error(
|
|
@@ -164,24 +188,40 @@ async function memSave(input, ctx) {
|
|
|
164
188
|
);
|
|
165
189
|
}
|
|
166
190
|
const existing = existsSync4(ctx.paths.memoriesDir) ? await loadMemoriesFromDir2(ctx.paths.memoriesDir) : [];
|
|
191
|
+
const haiveConfig = await loadConfig(ctx.paths);
|
|
192
|
+
const resolvedScope = input.scope ?? haiveConfig.defaultScope ?? "personal";
|
|
167
193
|
const invalidPaths = input.paths.filter(
|
|
168
194
|
(p) => !existsSync4(path3.resolve(ctx.paths.root, p))
|
|
169
195
|
);
|
|
170
196
|
const incomingHash = bodyHash(input.body);
|
|
171
197
|
const hashDuplicate = existing.find(
|
|
172
|
-
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope ===
|
|
198
|
+
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope === resolvedScope
|
|
173
199
|
);
|
|
174
200
|
if (hashDuplicate) {
|
|
175
201
|
throw new Error(
|
|
176
202
|
`Duplicate content detected \u2014 identical body already saved as "${hashDuplicate.memory.frontmatter.id}". Use mem_update to modify it, or change the body to add new information.`
|
|
177
203
|
);
|
|
178
204
|
}
|
|
205
|
+
const incomingTokens = bodyTokenSet(input.body);
|
|
206
|
+
function bodySimilarWarnings(excludeIds) {
|
|
207
|
+
const dup = maxBodySimilarity(incomingTokens, existing, resolvedScope, input.type, excludeIds);
|
|
208
|
+
if (!dup?.id) return {};
|
|
209
|
+
const body_similar = {
|
|
210
|
+
id: dup.id,
|
|
211
|
+
score: Math.round(dup.score * 100) / 100
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
similarityWarning: `Body is ~${Math.round(dup.score * 100)}% similar (token overlap) to existing "${dup.id}" \u2014 consolidate if redundant.`,
|
|
215
|
+
body_similar
|
|
216
|
+
};
|
|
217
|
+
}
|
|
179
218
|
if (input.topic) {
|
|
180
219
|
const topicMatch = existing.find(
|
|
181
|
-
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope ===
|
|
220
|
+
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === resolvedScope && (!input.module || memory.frontmatter.module === input.module)
|
|
182
221
|
);
|
|
183
222
|
if (topicMatch) {
|
|
184
223
|
const fm = topicMatch.memory.frontmatter;
|
|
224
|
+
const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
|
|
185
225
|
const newFrontmatter = {
|
|
186
226
|
...fm,
|
|
187
227
|
body: input.body,
|
|
@@ -198,18 +238,22 @@ async function memSave(input, ctx) {
|
|
|
198
238
|
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
199
239
|
"utf8"
|
|
200
240
|
);
|
|
241
|
+
const mergedTw = [
|
|
242
|
+
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
|
|
243
|
+
simW ?? null
|
|
244
|
+
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
201
245
|
return {
|
|
202
246
|
id: fm.id,
|
|
203
247
|
scope: fm.scope,
|
|
204
248
|
file_path: topicMatch.filePath,
|
|
205
249
|
action: "updated",
|
|
206
250
|
revision_count: newFrontmatter.revision_count,
|
|
207
|
-
...
|
|
251
|
+
...mergedTw ? { warning: mergedTw } : {},
|
|
252
|
+
...bs ? { body_similar: bs } : {},
|
|
253
|
+
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
208
254
|
};
|
|
209
255
|
}
|
|
210
256
|
}
|
|
211
|
-
const haiveConfig = await loadConfig(ctx.paths);
|
|
212
|
-
const resolvedScope = input.scope !== "personal" ? input.scope : haiveConfig.defaultScope ?? "personal";
|
|
213
257
|
const frontmatter = buildFrontmatter({
|
|
214
258
|
type: input.type,
|
|
215
259
|
slug: input.slug,
|
|
@@ -248,9 +292,11 @@ async function memSave(input, ctx) {
|
|
|
248
292
|
}
|
|
249
293
|
}
|
|
250
294
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
295
|
+
const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
|
|
251
296
|
const finalWarning = [
|
|
252
297
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
|
|
253
|
-
warning ?? null
|
|
298
|
+
warning ?? null,
|
|
299
|
+
simWarnNew ?? null
|
|
254
300
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
255
301
|
return {
|
|
256
302
|
id: frontmatter.id,
|
|
@@ -259,6 +305,7 @@ async function memSave(input, ctx) {
|
|
|
259
305
|
action: "created",
|
|
260
306
|
...finalWarning ? { warning: finalWarning } : {},
|
|
261
307
|
...similar_found ? { similar_found } : {},
|
|
308
|
+
...bsNew ? { body_similar: bsNew } : {},
|
|
262
309
|
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
263
310
|
};
|
|
264
311
|
}
|
|
@@ -1294,6 +1341,7 @@ import {
|
|
|
1294
1341
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
1295
1342
|
deriveConfidence as deriveConfidence4,
|
|
1296
1343
|
estimateTokens,
|
|
1344
|
+
extractActionsBriefBody,
|
|
1297
1345
|
getUsage as getUsage5,
|
|
1298
1346
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1299
1347
|
isAutoPromoteEligible,
|
|
@@ -1306,6 +1354,7 @@ import {
|
|
|
1306
1354
|
loadUsageIndex as loadUsageIndex7,
|
|
1307
1355
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1308
1356
|
queryCodeMap,
|
|
1357
|
+
resolveBriefingBudget,
|
|
1309
1358
|
serializeMemory as serializeMemory9,
|
|
1310
1359
|
tokenizeQuery as tokenizeQuery2,
|
|
1311
1360
|
trackReads as trackReads3,
|
|
@@ -1328,17 +1377,29 @@ var GetBriefingInputSchema = {
|
|
|
1328
1377
|
),
|
|
1329
1378
|
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1330
1379
|
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1331
|
-
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
1332
|
-
"Output format: 'full' returns
|
|
1380
|
+
format: z17.enum(["full", "compact", "actions"]).default("full").describe(
|
|
1381
|
+
"Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
|
|
1333
1382
|
),
|
|
1334
1383
|
symbols: z17.array(z17.string()).default([]).describe(
|
|
1335
1384
|
"Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `haive index code` to have been run."
|
|
1336
1385
|
),
|
|
1337
1386
|
min_semantic_score: z17.number().min(0).max(1).default(0).describe(
|
|
1338
1387
|
"Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
|
|
1388
|
+
),
|
|
1389
|
+
budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
1390
|
+
"Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
|
|
1339
1391
|
)
|
|
1340
1392
|
};
|
|
1393
|
+
var GetBriefingZod = z17.object(GetBriefingInputSchema);
|
|
1341
1394
|
async function getBriefing(input, ctx) {
|
|
1395
|
+
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
1396
|
+
max_tokens: input.max_tokens,
|
|
1397
|
+
max_memories: input.max_memories,
|
|
1398
|
+
include_module_contexts: input.include_module_contexts
|
|
1399
|
+
});
|
|
1400
|
+
const briefingMaxTokens = resolvedBudget.max_tokens;
|
|
1401
|
+
const briefingMaxMemories = resolvedBudget.max_memories;
|
|
1402
|
+
const briefingIncludeModules = resolvedBudget.include_module_contexts;
|
|
1342
1403
|
const inferred = inferModulesFromPaths2(input.files);
|
|
1343
1404
|
const memories = [];
|
|
1344
1405
|
let searchMode = "literal";
|
|
@@ -1450,8 +1511,8 @@ async function getBriefing(input, ctx) {
|
|
|
1450
1511
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1451
1512
|
return sb - sa;
|
|
1452
1513
|
});
|
|
1453
|
-
for (const mem of ranked.slice(0,
|
|
1454
|
-
if (seen.size >=
|
|
1514
|
+
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
1515
|
+
if (seen.size >= briefingMaxMemories * 2) break;
|
|
1455
1516
|
const loaded = byId.get(mem.id);
|
|
1456
1517
|
if (!loaded) continue;
|
|
1457
1518
|
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
@@ -1460,12 +1521,13 @@ async function getBriefing(input, ctx) {
|
|
|
1460
1521
|
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
1461
1522
|
}
|
|
1462
1523
|
}
|
|
1463
|
-
memories.push(...ranked.slice(0,
|
|
1524
|
+
memories.push(...ranked.slice(0, briefingMaxMemories));
|
|
1464
1525
|
if (input.track && memories.length > 0) {
|
|
1465
1526
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1466
1527
|
const freshUsage = await loadUsageIndex7(ctx.paths);
|
|
1528
|
+
const cfg = await loadConfig3(ctx.paths);
|
|
1467
1529
|
const rule = {
|
|
1468
|
-
minReads: DEFAULT_AUTO_PROMOTE_RULE.minReads,
|
|
1530
|
+
minReads: cfg.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads,
|
|
1469
1531
|
maxRejections: DEFAULT_AUTO_PROMOTE_RULE.maxRejections
|
|
1470
1532
|
};
|
|
1471
1533
|
for (const m of memories) {
|
|
@@ -1539,7 +1601,7 @@ async function getBriefing(input, ctx) {
|
|
|
1539
1601
|
}
|
|
1540
1602
|
}
|
|
1541
1603
|
}
|
|
1542
|
-
const moduleContents =
|
|
1604
|
+
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1543
1605
|
const memoriesText = memories.map((m) => {
|
|
1544
1606
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
1545
1607
|
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
@@ -1557,7 +1619,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1557
1619
|
},
|
|
1558
1620
|
{ key: "memories", text: memoriesText, weight: 4, mode: "head" }
|
|
1559
1621
|
],
|
|
1560
|
-
|
|
1622
|
+
briefingMaxTokens
|
|
1561
1623
|
);
|
|
1562
1624
|
const projectSlice = slices.find((s) => s.key === "project");
|
|
1563
1625
|
const modulesSlice = slices.find((s) => s.key === "modules");
|
|
@@ -1599,7 +1661,10 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1599
1661
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1600
1662
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1601
1663
|
}
|
|
1602
|
-
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories
|
|
1664
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
1665
|
+
...m,
|
|
1666
|
+
body: extractActionsBriefBody(m.body)
|
|
1667
|
+
})) : trimmedMemories;
|
|
1603
1668
|
let symbolLocations;
|
|
1604
1669
|
const symbolsToLookup = new Set(input.symbols);
|
|
1605
1670
|
for (const m of outputMemories) {
|
|
@@ -1721,6 +1786,11 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1721
1786
|
"After completing the task: capture new gotchas with mem_observe, failed approaches with mem_tried, validated patterns with mem_save."
|
|
1722
1787
|
);
|
|
1723
1788
|
}
|
|
1789
|
+
if (outputMemories.length > 2 && !input.budget_preset && input.task && !hints.some((h) => h.includes("budget_preset"))) {
|
|
1790
|
+
hints.push(
|
|
1791
|
+
"For tighter token budgets on small tasks pass budget_preset:'quick'; for refactor-sized work use budget_preset:'deep'."
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1724
1794
|
}
|
|
1725
1795
|
return {
|
|
1726
1796
|
...input.task ? { task: input.task } : {},
|
|
@@ -1743,7 +1813,8 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1743
1813
|
...hints.length > 0 ? { hints } : {},
|
|
1744
1814
|
estimated_tokens: totalTokens,
|
|
1745
1815
|
budget: {
|
|
1746
|
-
max_tokens:
|
|
1816
|
+
max_tokens: briefingMaxTokens,
|
|
1817
|
+
...input.budget_preset ? { preset_applied: input.budget_preset } : {},
|
|
1747
1818
|
spent: {
|
|
1748
1819
|
project: projectSlice.estimatedTokens,
|
|
1749
1820
|
modules: modulesSlice.estimatedTokens,
|
|
@@ -1956,7 +2027,7 @@ var MemRelevantToInputSchema = {
|
|
|
1956
2027
|
files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
1957
2028
|
limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
1958
2029
|
min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
1959
|
-
format: z21.enum(["full", "compact"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies.")
|
|
2030
|
+
format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
1960
2031
|
};
|
|
1961
2032
|
async function memRelevantTo(input, ctx) {
|
|
1962
2033
|
const briefingInput = {
|
|
@@ -2813,7 +2884,9 @@ async function patternDetect(input, ctx) {
|
|
|
2813
2884
|
for (const file of configFiles.slice(0, 5)) {
|
|
2814
2885
|
const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
|
|
2815
2886
|
if (!diff) continue;
|
|
2816
|
-
const
|
|
2887
|
+
const parentDir = path11.basename(path11.dirname(file));
|
|
2888
|
+
const baseName = path11.basename(file).replace(/\.[^.]+$/, "");
|
|
2889
|
+
const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
|
|
2817
2890
|
matches.push({
|
|
2818
2891
|
kind: "config_change",
|
|
2819
2892
|
signal: `Config file modified: ${file}`,
|
|
@@ -3212,7 +3285,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3212
3285
|
|
|
3213
3286
|
// src/server.ts
|
|
3214
3287
|
var SERVER_NAME = "haive";
|
|
3215
|
-
var SERVER_VERSION = "0.9.
|
|
3288
|
+
var SERVER_VERSION = "0.9.3";
|
|
3216
3289
|
function jsonResult(data) {
|
|
3217
3290
|
return {
|
|
3218
3291
|
content: [
|
|
@@ -3378,7 +3451,8 @@ function createHaiveServer(options = {}) {
|
|
|
3378
3451
|
" task \u2014 what you are about to do (1\u20132 sentences) \u2014 ALWAYS provide this",
|
|
3379
3452
|
" files \u2014 files you are about to edit \u2014 surfaces anchored memories",
|
|
3380
3453
|
" symbols \u2014 symbol names to look up in the code-map (e.g. ['PaymentService'])",
|
|
3381
|
-
" format \u2014 'full' (default) | 'compact' (1-line
|
|
3454
|
+
" format \u2014 'full' (default) | 'compact' (1-line) | 'actions' (bullet-first excerpts)",
|
|
3455
|
+
" budget_preset \u2014 'quick' | 'balanced' | 'deep' \u2014 scales max_tokens/memories/module contexts",
|
|
3382
3456
|
"",
|
|
3383
3457
|
"EXAMPLE USAGE:",
|
|
3384
3458
|
" get_briefing({ task: 'add a Stripe payment integration', files: ['src/payments/'], symbols: ['PaymentService'] })",
|
|
@@ -3684,6 +3758,7 @@ function createHaiveServer(options = {}) {
|
|
|
3684
3758
|
" files \u2014 files you'll edit (surfaces anchored memories)",
|
|
3685
3759
|
" limit \u2014 cap on returned memories (default 8)",
|
|
3686
3760
|
" min_semantic_score \u2014 drop weak semantic hits below this cosine (default 0.25)",
|
|
3761
|
+
" format \u2014 'full' | 'compact' | 'actions' (inherits get_briefing memory framing)",
|
|
3687
3762
|
"",
|
|
3688
3763
|
"RETURNS: { task, search_mode, memories: [...], hints?: [...], empty?: true }"
|
|
3689
3764
|
].join("\n"),
|
|
@@ -3941,9 +4016,13 @@ function createHaiveServer(options = {}) {
|
|
|
3941
4016
|
);
|
|
3942
4017
|
return { server, context, tracker };
|
|
3943
4018
|
}
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
4019
|
+
function parseMcpCliArgs(argv) {
|
|
4020
|
+
for (let i = 2; i < argv.length; i++) {
|
|
4021
|
+
const arg = argv[i];
|
|
4022
|
+
if (arg === "--version" || arg === "-V") {
|
|
4023
|
+
return { versionOnly: true };
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
3947
4026
|
const out = {};
|
|
3948
4027
|
for (let i = 2; i < argv.length; i++) {
|
|
3949
4028
|
const arg = argv[i];
|
|
@@ -3953,18 +4032,26 @@ function parseArgs(argv) {
|
|
|
3953
4032
|
out.root = arg.slice("--root=".length);
|
|
3954
4033
|
}
|
|
3955
4034
|
}
|
|
3956
|
-
return out;
|
|
4035
|
+
return { root: out.root, versionOnly: false };
|
|
3957
4036
|
}
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
4037
|
+
function printHaiveMcpVersion() {
|
|
4038
|
+
console.log(SERVER_VERSION);
|
|
4039
|
+
}
|
|
4040
|
+
async function runHaiveMcpStdio(options) {
|
|
4041
|
+
const { server, context } = createHaiveServer({ root: options.root });
|
|
3961
4042
|
console.error(
|
|
3962
4043
|
`[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
|
|
3963
4044
|
);
|
|
3964
|
-
|
|
3965
|
-
|
|
4045
|
+
await server.connect(new StdioServerTransport());
|
|
4046
|
+
}
|
|
4047
|
+
|
|
4048
|
+
// src/index.ts
|
|
4049
|
+
var parsed = parseMcpCliArgs(process.argv);
|
|
4050
|
+
if (parsed.versionOnly) {
|
|
4051
|
+
printHaiveMcpVersion();
|
|
4052
|
+
process.exit(0);
|
|
3966
4053
|
}
|
|
3967
|
-
|
|
4054
|
+
runHaiveMcpStdio({ root: parsed.root }).catch((err) => {
|
|
3968
4055
|
console.error("[haive-mcp] fatal:", err instanceof Error ? err.message : err);
|
|
3969
4056
|
process.exit(1);
|
|
3970
4057
|
});
|