@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/dist/server.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ declare class SessionTracker {
|
|
|
23
23
|
private registerShutdownHandler;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/** Single inferred type — optional keys are optional (not `| undefined` required everywhere). */
|
|
27
|
+
declare const GetBriefingZod: z.ZodObject<{
|
|
27
28
|
task: z.ZodOptional<z.ZodString>;
|
|
28
29
|
files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
29
30
|
max_tokens: z.ZodDefault<z.ZodNumber>;
|
|
@@ -33,13 +34,40 @@ declare const GetBriefingInputSchema: {
|
|
|
33
34
|
semantic: z.ZodDefault<z.ZodBoolean>;
|
|
34
35
|
include_stale: z.ZodDefault<z.ZodBoolean>;
|
|
35
36
|
track: z.ZodDefault<z.ZodBoolean>;
|
|
36
|
-
format: z.ZodDefault<z.ZodEnum<["full", "compact"]>>;
|
|
37
|
+
format: z.ZodDefault<z.ZodEnum<["full", "compact", "actions"]>>;
|
|
37
38
|
symbols: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
38
39
|
min_semantic_score: z.ZodDefault<z.ZodNumber>;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
budget_preset: z.ZodOptional<z.ZodEnum<["quick", "balanced", "deep"]>>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
symbols: string[];
|
|
43
|
+
files: string[];
|
|
44
|
+
max_tokens: number;
|
|
45
|
+
max_memories: number;
|
|
46
|
+
include_project_context: boolean;
|
|
47
|
+
include_module_contexts: boolean;
|
|
48
|
+
semantic: boolean;
|
|
49
|
+
include_stale: boolean;
|
|
50
|
+
track: boolean;
|
|
51
|
+
format: "full" | "compact" | "actions";
|
|
52
|
+
min_semantic_score: number;
|
|
53
|
+
task?: string | undefined;
|
|
54
|
+
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
55
|
+
}, {
|
|
56
|
+
symbols?: string[] | undefined;
|
|
57
|
+
task?: string | undefined;
|
|
58
|
+
files?: string[] | undefined;
|
|
59
|
+
max_tokens?: number | undefined;
|
|
60
|
+
max_memories?: number | undefined;
|
|
61
|
+
include_project_context?: boolean | undefined;
|
|
62
|
+
include_module_contexts?: boolean | undefined;
|
|
63
|
+
semantic?: boolean | undefined;
|
|
64
|
+
include_stale?: boolean | undefined;
|
|
65
|
+
track?: boolean | undefined;
|
|
66
|
+
format?: "full" | "compact" | "actions" | undefined;
|
|
67
|
+
min_semantic_score?: number | undefined;
|
|
68
|
+
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
69
|
+
}>;
|
|
70
|
+
type GetBriefingInput = z.infer<typeof GetBriefingZod>;
|
|
43
71
|
interface BriefingMemory {
|
|
44
72
|
id: string;
|
|
45
73
|
scope: string;
|
|
@@ -133,6 +161,7 @@ interface BriefingOutput {
|
|
|
133
161
|
modules: number;
|
|
134
162
|
memories: number;
|
|
135
163
|
};
|
|
164
|
+
preset_applied?: "quick" | "balanced" | "deep";
|
|
136
165
|
};
|
|
137
166
|
}
|
|
138
167
|
declare function getBriefing(input: GetBriefingInput, ctx: HaiveContext): Promise<BriefingOutput>;
|
|
@@ -198,7 +227,7 @@ declare const MemRelevantToInputSchema: {
|
|
|
198
227
|
files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
199
228
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
200
229
|
min_semantic_score: z.ZodDefault<z.ZodNumber>;
|
|
201
|
-
format: z.ZodDefault<z.ZodEnum<["full", "compact"]>>;
|
|
230
|
+
format: z.ZodDefault<z.ZodEnum<["full", "compact", "actions"]>>;
|
|
202
231
|
};
|
|
203
232
|
type MemRelevantToInput = {
|
|
204
233
|
[K in keyof typeof MemRelevantToInputSchema]: z.infer<(typeof MemRelevantToInputSchema)[K]>;
|
|
@@ -543,5 +572,19 @@ declare function createHaiveServer(options?: CreateContextOptions): {
|
|
|
543
572
|
context: HaiveContext;
|
|
544
573
|
tracker: SessionTracker;
|
|
545
574
|
};
|
|
575
|
+
/** Parse argv for the standalone haive-mcp binary / CLI subprocess parity. */
|
|
576
|
+
declare function parseMcpCliArgs(argv: string[]): {
|
|
577
|
+
root?: string;
|
|
578
|
+
versionOnly: boolean;
|
|
579
|
+
};
|
|
580
|
+
/** Print MCP server version (same as haive CLI when bundled together). */
|
|
581
|
+
declare function printHaiveMcpVersion(): void;
|
|
582
|
+
/**
|
|
583
|
+
* Run the MCP server over stdio. Used by `haive-mcp` and by `haive mcp --stdio`
|
|
584
|
+
* when the MCP implementation is bundled into the CLI.
|
|
585
|
+
*/
|
|
586
|
+
declare function runHaiveMcpStdio(options: {
|
|
587
|
+
root?: string;
|
|
588
|
+
}): Promise<void>;
|
|
546
589
|
|
|
547
|
-
export { type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, type MemConflictsInput, type MemConflictsOutput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type PatternDetectInput, type PatternDetectOutput, type PreCommitCheckInput, type PreCommitCheckOutput, SERVER_NAME, SERVER_VERSION, type WhyThisDecisionInput, type WhyThisDecisionOutput, type WhyThisFileInput, type WhyThisFileOutput, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, getBriefing, getRecap, memConflicts, memDistill, memRelevantTo, patternDetect, preCommitCheck, whyThisDecision, whyThisFile };
|
|
590
|
+
export { type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, type MemConflictsInput, type MemConflictsOutput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type PatternDetectInput, type PatternDetectOutput, type PreCommitCheckInput, type PreCommitCheckOutput, SERVER_NAME, SERVER_VERSION, type WhyThisDecisionInput, type WhyThisDecisionOutput, type WhyThisFileInput, type WhyThisFileOutput, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, getBriefing, getRecap, memConflicts, memDistill, memRelevantTo, parseMcpCliArgs, patternDetect, preCommitCheck, printHaiveMcpVersion, runHaiveMcpStdio, whyThisDecision, whyThisFile };
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/server.ts
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
4
|
|
|
4
5
|
// src/context.ts
|
|
5
6
|
import { findProjectRoot, resolveHaivePaths } from "@hiveai/core";
|
|
@@ -137,7 +138,9 @@ var MemSaveInputSchema = {
|
|
|
137
138
|
),
|
|
138
139
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
139
140
|
body: z4.string().describe("Markdown body of the memory"),
|
|
140
|
-
scope: z4.enum(["personal", "team", "module"]).
|
|
141
|
+
scope: z4.enum(["personal", "team", "module"]).optional().describe(
|
|
142
|
+
"Visibility scope: personal | team | module. When omitted, falls back to defaultScope in haive.config.json (default: personal)."
|
|
143
|
+
),
|
|
141
144
|
module: z4.string().optional().describe("Module name (required when scope=module)"),
|
|
142
145
|
tags: z4.array(z4.string()).default([]).describe("Tags for filtering"),
|
|
143
146
|
domain: z4.string().optional().describe("Domain (e.g. transactions, billing)"),
|
|
@@ -152,6 +155,30 @@ var MemSaveInputSchema = {
|
|
|
152
155
|
function bodyHash(body) {
|
|
153
156
|
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
154
157
|
}
|
|
158
|
+
var WORD_RE = /\b[a-z0-9]{3,}\b/gi;
|
|
159
|
+
function bodyTokenSet(body) {
|
|
160
|
+
const raw = body.toLowerCase().match(WORD_RE) ?? [];
|
|
161
|
+
return new Set(raw);
|
|
162
|
+
}
|
|
163
|
+
function maxBodySimilarity(incomingTokens, memories, scope, type, excludeIds) {
|
|
164
|
+
if (incomingTokens.size < 6) return null;
|
|
165
|
+
let best = null;
|
|
166
|
+
const skip = excludeIds ?? /* @__PURE__ */ new Set();
|
|
167
|
+
for (const { memory } of memories) {
|
|
168
|
+
const fm = memory.frontmatter;
|
|
169
|
+
if (skip.has(fm.id)) continue;
|
|
170
|
+
if (fm.scope !== scope || fm.type !== type) continue;
|
|
171
|
+
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
172
|
+
const other = bodyTokenSet(memory.body);
|
|
173
|
+
if (other.size === 0) continue;
|
|
174
|
+
let inter = 0;
|
|
175
|
+
for (const t of incomingTokens) if (other.has(t)) inter++;
|
|
176
|
+
const uni = incomingTokens.size + other.size - inter;
|
|
177
|
+
const j = uni === 0 ? 0 : inter / uni;
|
|
178
|
+
if (j >= 0.72 && (!best || j > best.score)) best = { score: j, id: fm.id };
|
|
179
|
+
}
|
|
180
|
+
return best;
|
|
181
|
+
}
|
|
155
182
|
async function memSave(input, ctx) {
|
|
156
183
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
157
184
|
throw new Error(
|
|
@@ -159,24 +186,40 @@ async function memSave(input, ctx) {
|
|
|
159
186
|
);
|
|
160
187
|
}
|
|
161
188
|
const existing = existsSync4(ctx.paths.memoriesDir) ? await loadMemoriesFromDir2(ctx.paths.memoriesDir) : [];
|
|
189
|
+
const haiveConfig = await loadConfig(ctx.paths);
|
|
190
|
+
const resolvedScope = input.scope ?? haiveConfig.defaultScope ?? "personal";
|
|
162
191
|
const invalidPaths = input.paths.filter(
|
|
163
192
|
(p) => !existsSync4(path3.resolve(ctx.paths.root, p))
|
|
164
193
|
);
|
|
165
194
|
const incomingHash = bodyHash(input.body);
|
|
166
195
|
const hashDuplicate = existing.find(
|
|
167
|
-
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope ===
|
|
196
|
+
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope === resolvedScope
|
|
168
197
|
);
|
|
169
198
|
if (hashDuplicate) {
|
|
170
199
|
throw new Error(
|
|
171
200
|
`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.`
|
|
172
201
|
);
|
|
173
202
|
}
|
|
203
|
+
const incomingTokens = bodyTokenSet(input.body);
|
|
204
|
+
function bodySimilarWarnings(excludeIds) {
|
|
205
|
+
const dup = maxBodySimilarity(incomingTokens, existing, resolvedScope, input.type, excludeIds);
|
|
206
|
+
if (!dup?.id) return {};
|
|
207
|
+
const body_similar = {
|
|
208
|
+
id: dup.id,
|
|
209
|
+
score: Math.round(dup.score * 100) / 100
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
similarityWarning: `Body is ~${Math.round(dup.score * 100)}% similar (token overlap) to existing "${dup.id}" \u2014 consolidate if redundant.`,
|
|
213
|
+
body_similar
|
|
214
|
+
};
|
|
215
|
+
}
|
|
174
216
|
if (input.topic) {
|
|
175
217
|
const topicMatch = existing.find(
|
|
176
|
-
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope ===
|
|
218
|
+
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === resolvedScope && (!input.module || memory.frontmatter.module === input.module)
|
|
177
219
|
);
|
|
178
220
|
if (topicMatch) {
|
|
179
221
|
const fm = topicMatch.memory.frontmatter;
|
|
222
|
+
const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
|
|
180
223
|
const newFrontmatter = {
|
|
181
224
|
...fm,
|
|
182
225
|
body: input.body,
|
|
@@ -193,18 +236,22 @@ async function memSave(input, ctx) {
|
|
|
193
236
|
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
194
237
|
"utf8"
|
|
195
238
|
);
|
|
239
|
+
const mergedTw = [
|
|
240
|
+
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
|
|
241
|
+
simW ?? null
|
|
242
|
+
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
196
243
|
return {
|
|
197
244
|
id: fm.id,
|
|
198
245
|
scope: fm.scope,
|
|
199
246
|
file_path: topicMatch.filePath,
|
|
200
247
|
action: "updated",
|
|
201
248
|
revision_count: newFrontmatter.revision_count,
|
|
202
|
-
...
|
|
249
|
+
...mergedTw ? { warning: mergedTw } : {},
|
|
250
|
+
...bs ? { body_similar: bs } : {},
|
|
251
|
+
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
203
252
|
};
|
|
204
253
|
}
|
|
205
254
|
}
|
|
206
|
-
const haiveConfig = await loadConfig(ctx.paths);
|
|
207
|
-
const resolvedScope = input.scope !== "personal" ? input.scope : haiveConfig.defaultScope ?? "personal";
|
|
208
255
|
const frontmatter = buildFrontmatter({
|
|
209
256
|
type: input.type,
|
|
210
257
|
slug: input.slug,
|
|
@@ -243,9 +290,11 @@ async function memSave(input, ctx) {
|
|
|
243
290
|
}
|
|
244
291
|
}
|
|
245
292
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
293
|
+
const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
|
|
246
294
|
const finalWarning = [
|
|
247
295
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
|
|
248
|
-
warning ?? null
|
|
296
|
+
warning ?? null,
|
|
297
|
+
simWarnNew ?? null
|
|
249
298
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
250
299
|
return {
|
|
251
300
|
id: frontmatter.id,
|
|
@@ -254,6 +303,7 @@ async function memSave(input, ctx) {
|
|
|
254
303
|
action: "created",
|
|
255
304
|
...finalWarning ? { warning: finalWarning } : {},
|
|
256
305
|
...similar_found ? { similar_found } : {},
|
|
306
|
+
...bsNew ? { body_similar: bsNew } : {},
|
|
257
307
|
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
258
308
|
};
|
|
259
309
|
}
|
|
@@ -1289,6 +1339,7 @@ import {
|
|
|
1289
1339
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
1290
1340
|
deriveConfidence as deriveConfidence4,
|
|
1291
1341
|
estimateTokens,
|
|
1342
|
+
extractActionsBriefBody,
|
|
1292
1343
|
getUsage as getUsage5,
|
|
1293
1344
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1294
1345
|
isAutoPromoteEligible,
|
|
@@ -1301,6 +1352,7 @@ import {
|
|
|
1301
1352
|
loadUsageIndex as loadUsageIndex7,
|
|
1302
1353
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1303
1354
|
queryCodeMap,
|
|
1355
|
+
resolveBriefingBudget,
|
|
1304
1356
|
serializeMemory as serializeMemory9,
|
|
1305
1357
|
tokenizeQuery as tokenizeQuery2,
|
|
1306
1358
|
trackReads as trackReads3,
|
|
@@ -1323,17 +1375,29 @@ var GetBriefingInputSchema = {
|
|
|
1323
1375
|
),
|
|
1324
1376
|
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1325
1377
|
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1326
|
-
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
1327
|
-
"Output format: 'full' returns
|
|
1378
|
+
format: z17.enum(["full", "compact", "actions"]).default("full").describe(
|
|
1379
|
+
"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."
|
|
1328
1380
|
),
|
|
1329
1381
|
symbols: z17.array(z17.string()).default([]).describe(
|
|
1330
1382
|
"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."
|
|
1331
1383
|
),
|
|
1332
1384
|
min_semantic_score: z17.number().min(0).max(1).default(0).describe(
|
|
1333
1385
|
"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."
|
|
1386
|
+
),
|
|
1387
|
+
budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
1388
|
+
"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."
|
|
1334
1389
|
)
|
|
1335
1390
|
};
|
|
1391
|
+
var GetBriefingZod = z17.object(GetBriefingInputSchema);
|
|
1336
1392
|
async function getBriefing(input, ctx) {
|
|
1393
|
+
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
1394
|
+
max_tokens: input.max_tokens,
|
|
1395
|
+
max_memories: input.max_memories,
|
|
1396
|
+
include_module_contexts: input.include_module_contexts
|
|
1397
|
+
});
|
|
1398
|
+
const briefingMaxTokens = resolvedBudget.max_tokens;
|
|
1399
|
+
const briefingMaxMemories = resolvedBudget.max_memories;
|
|
1400
|
+
const briefingIncludeModules = resolvedBudget.include_module_contexts;
|
|
1337
1401
|
const inferred = inferModulesFromPaths2(input.files);
|
|
1338
1402
|
const memories = [];
|
|
1339
1403
|
let searchMode = "literal";
|
|
@@ -1445,8 +1509,8 @@ async function getBriefing(input, ctx) {
|
|
|
1445
1509
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1446
1510
|
return sb - sa;
|
|
1447
1511
|
});
|
|
1448
|
-
for (const mem of ranked.slice(0,
|
|
1449
|
-
if (seen.size >=
|
|
1512
|
+
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
1513
|
+
if (seen.size >= briefingMaxMemories * 2) break;
|
|
1450
1514
|
const loaded = byId.get(mem.id);
|
|
1451
1515
|
if (!loaded) continue;
|
|
1452
1516
|
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
@@ -1455,12 +1519,13 @@ async function getBriefing(input, ctx) {
|
|
|
1455
1519
|
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
1456
1520
|
}
|
|
1457
1521
|
}
|
|
1458
|
-
memories.push(...ranked.slice(0,
|
|
1522
|
+
memories.push(...ranked.slice(0, briefingMaxMemories));
|
|
1459
1523
|
if (input.track && memories.length > 0) {
|
|
1460
1524
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1461
1525
|
const freshUsage = await loadUsageIndex7(ctx.paths);
|
|
1526
|
+
const cfg = await loadConfig3(ctx.paths);
|
|
1462
1527
|
const rule = {
|
|
1463
|
-
minReads: DEFAULT_AUTO_PROMOTE_RULE.minReads,
|
|
1528
|
+
minReads: cfg.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads,
|
|
1464
1529
|
maxRejections: DEFAULT_AUTO_PROMOTE_RULE.maxRejections
|
|
1465
1530
|
};
|
|
1466
1531
|
for (const m of memories) {
|
|
@@ -1534,7 +1599,7 @@ async function getBriefing(input, ctx) {
|
|
|
1534
1599
|
}
|
|
1535
1600
|
}
|
|
1536
1601
|
}
|
|
1537
|
-
const moduleContents =
|
|
1602
|
+
const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1538
1603
|
const memoriesText = memories.map((m) => {
|
|
1539
1604
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
1540
1605
|
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
@@ -1552,7 +1617,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1552
1617
|
},
|
|
1553
1618
|
{ key: "memories", text: memoriesText, weight: 4, mode: "head" }
|
|
1554
1619
|
],
|
|
1555
|
-
|
|
1620
|
+
briefingMaxTokens
|
|
1556
1621
|
);
|
|
1557
1622
|
const projectSlice = slices.find((s) => s.key === "project");
|
|
1558
1623
|
const modulesSlice = slices.find((s) => s.key === "modules");
|
|
@@ -1594,7 +1659,10 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1594
1659
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1595
1660
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1596
1661
|
}
|
|
1597
|
-
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories
|
|
1662
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
|
|
1663
|
+
...m,
|
|
1664
|
+
body: extractActionsBriefBody(m.body)
|
|
1665
|
+
})) : trimmedMemories;
|
|
1598
1666
|
let symbolLocations;
|
|
1599
1667
|
const symbolsToLookup = new Set(input.symbols);
|
|
1600
1668
|
for (const m of outputMemories) {
|
|
@@ -1716,6 +1784,11 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1716
1784
|
"After completing the task: capture new gotchas with mem_observe, failed approaches with mem_tried, validated patterns with mem_save."
|
|
1717
1785
|
);
|
|
1718
1786
|
}
|
|
1787
|
+
if (outputMemories.length > 2 && !input.budget_preset && input.task && !hints.some((h) => h.includes("budget_preset"))) {
|
|
1788
|
+
hints.push(
|
|
1789
|
+
"For tighter token budgets on small tasks pass budget_preset:'quick'; for refactor-sized work use budget_preset:'deep'."
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1719
1792
|
}
|
|
1720
1793
|
return {
|
|
1721
1794
|
...input.task ? { task: input.task } : {},
|
|
@@ -1738,7 +1811,8 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1738
1811
|
...hints.length > 0 ? { hints } : {},
|
|
1739
1812
|
estimated_tokens: totalTokens,
|
|
1740
1813
|
budget: {
|
|
1741
|
-
max_tokens:
|
|
1814
|
+
max_tokens: briefingMaxTokens,
|
|
1815
|
+
...input.budget_preset ? { preset_applied: input.budget_preset } : {},
|
|
1742
1816
|
spent: {
|
|
1743
1817
|
project: projectSlice.estimatedTokens,
|
|
1744
1818
|
modules: modulesSlice.estimatedTokens,
|
|
@@ -1951,7 +2025,7 @@ var MemRelevantToInputSchema = {
|
|
|
1951
2025
|
files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
1952
2026
|
limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
1953
2027
|
min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
1954
|
-
format: z21.enum(["full", "compact"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies.")
|
|
2028
|
+
format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
1955
2029
|
};
|
|
1956
2030
|
async function memRelevantTo(input, ctx) {
|
|
1957
2031
|
const briefingInput = {
|
|
@@ -2808,7 +2882,9 @@ async function patternDetect(input, ctx) {
|
|
|
2808
2882
|
for (const file of configFiles.slice(0, 5)) {
|
|
2809
2883
|
const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
|
|
2810
2884
|
if (!diff) continue;
|
|
2811
|
-
const
|
|
2885
|
+
const parentDir = path11.basename(path11.dirname(file));
|
|
2886
|
+
const baseName = path11.basename(file).replace(/\.[^.]+$/, "");
|
|
2887
|
+
const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
|
|
2812
2888
|
matches.push({
|
|
2813
2889
|
kind: "config_change",
|
|
2814
2890
|
signal: `Config file modified: ${file}`,
|
|
@@ -3207,7 +3283,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3207
3283
|
|
|
3208
3284
|
// src/server.ts
|
|
3209
3285
|
var SERVER_NAME = "haive";
|
|
3210
|
-
var SERVER_VERSION = "0.9.
|
|
3286
|
+
var SERVER_VERSION = "0.9.3";
|
|
3211
3287
|
function jsonResult(data) {
|
|
3212
3288
|
return {
|
|
3213
3289
|
content: [
|
|
@@ -3373,7 +3449,8 @@ function createHaiveServer(options = {}) {
|
|
|
3373
3449
|
" task \u2014 what you are about to do (1\u20132 sentences) \u2014 ALWAYS provide this",
|
|
3374
3450
|
" files \u2014 files you are about to edit \u2014 surfaces anchored memories",
|
|
3375
3451
|
" symbols \u2014 symbol names to look up in the code-map (e.g. ['PaymentService'])",
|
|
3376
|
-
" format \u2014 'full' (default) | 'compact' (1-line
|
|
3452
|
+
" format \u2014 'full' (default) | 'compact' (1-line) | 'actions' (bullet-first excerpts)",
|
|
3453
|
+
" budget_preset \u2014 'quick' | 'balanced' | 'deep' \u2014 scales max_tokens/memories/module contexts",
|
|
3377
3454
|
"",
|
|
3378
3455
|
"EXAMPLE USAGE:",
|
|
3379
3456
|
" get_briefing({ task: 'add a Stripe payment integration', files: ['src/payments/'], symbols: ['PaymentService'] })",
|
|
@@ -3679,6 +3756,7 @@ function createHaiveServer(options = {}) {
|
|
|
3679
3756
|
" files \u2014 files you'll edit (surfaces anchored memories)",
|
|
3680
3757
|
" limit \u2014 cap on returned memories (default 8)",
|
|
3681
3758
|
" min_semantic_score \u2014 drop weak semantic hits below this cosine (default 0.25)",
|
|
3759
|
+
" format \u2014 'full' | 'compact' | 'actions' (inherits get_briefing memory framing)",
|
|
3682
3760
|
"",
|
|
3683
3761
|
"RETURNS: { task, search_mode, memories: [...], hints?: [...], empty?: true }"
|
|
3684
3762
|
].join("\n"),
|
|
@@ -3936,6 +4014,34 @@ function createHaiveServer(options = {}) {
|
|
|
3936
4014
|
);
|
|
3937
4015
|
return { server, context, tracker };
|
|
3938
4016
|
}
|
|
4017
|
+
function parseMcpCliArgs(argv) {
|
|
4018
|
+
for (let i = 2; i < argv.length; i++) {
|
|
4019
|
+
const arg = argv[i];
|
|
4020
|
+
if (arg === "--version" || arg === "-V") {
|
|
4021
|
+
return { versionOnly: true };
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
const out = {};
|
|
4025
|
+
for (let i = 2; i < argv.length; i++) {
|
|
4026
|
+
const arg = argv[i];
|
|
4027
|
+
if (arg === "--root" || arg === "-r") {
|
|
4028
|
+
out.root = argv[++i];
|
|
4029
|
+
} else if (arg?.startsWith("--root=")) {
|
|
4030
|
+
out.root = arg.slice("--root=".length);
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
return { root: out.root, versionOnly: false };
|
|
4034
|
+
}
|
|
4035
|
+
function printHaiveMcpVersion() {
|
|
4036
|
+
console.log(SERVER_VERSION);
|
|
4037
|
+
}
|
|
4038
|
+
async function runHaiveMcpStdio(options) {
|
|
4039
|
+
const { server, context } = createHaiveServer({ root: options.root });
|
|
4040
|
+
console.error(
|
|
4041
|
+
`[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
|
|
4042
|
+
);
|
|
4043
|
+
await server.connect(new StdioServerTransport());
|
|
4044
|
+
}
|
|
3939
4045
|
export {
|
|
3940
4046
|
SERVER_NAME,
|
|
3941
4047
|
SERVER_VERSION,
|
|
@@ -3948,8 +4054,11 @@ export {
|
|
|
3948
4054
|
memConflicts,
|
|
3949
4055
|
memDistill,
|
|
3950
4056
|
memRelevantTo,
|
|
4057
|
+
parseMcpCliArgs,
|
|
3951
4058
|
patternDetect,
|
|
3952
4059
|
preCommitCheck,
|
|
4060
|
+
printHaiveMcpVersion,
|
|
4061
|
+
runHaiveMcpStdio,
|
|
3953
4062
|
whyThisDecision,
|
|
3954
4063
|
whyThisFile
|
|
3955
4064
|
};
|