@hiveai/mcp 0.2.7 → 0.2.8
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/dist/index.js +170 -12
- package/dist/index.js.map +1 -1
- package/dist/server.js +170 -12
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -392,6 +392,7 @@ async function memVerify(input, ctx) {
|
|
|
392
392
|
file_path: filePath,
|
|
393
393
|
stale: result.stale,
|
|
394
394
|
reason: result.reason,
|
|
395
|
+
...result.possibleRenames.length > 0 ? { possible_renames: result.possibleRenames } : {},
|
|
395
396
|
status_after: statusAfter
|
|
396
397
|
});
|
|
397
398
|
}
|
|
@@ -854,6 +855,7 @@ import {
|
|
|
854
855
|
estimateTokens,
|
|
855
856
|
getUsage as getUsage5,
|
|
856
857
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
858
|
+
isDecaying,
|
|
857
859
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
858
860
|
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
859
861
|
loadUsageIndex as loadUsageIndex7,
|
|
@@ -878,12 +880,17 @@ var GetBriefingInputSchema = {
|
|
|
878
880
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
879
881
|
),
|
|
880
882
|
include_stale: z15.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
881
|
-
track: z15.boolean().default(true).describe("Increment read_count on returned memories")
|
|
883
|
+
track: z15.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
884
|
+
format: z15.enum(["full", "compact"]).default("full").describe(
|
|
885
|
+
"Output format: 'full' returns complete memory bodies; 'compact' returns id + 1-line summary only (call mem_get for details)."
|
|
886
|
+
)
|
|
882
887
|
};
|
|
883
888
|
async function getBriefing(input, ctx) {
|
|
884
889
|
const inferred = inferModulesFromPaths2(input.files);
|
|
885
890
|
const memories = [];
|
|
886
891
|
let searchMode = "literal";
|
|
892
|
+
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
893
|
+
let byId = /* @__PURE__ */ new Map();
|
|
887
894
|
if (existsSync15(ctx.paths.memoriesDir)) {
|
|
888
895
|
const allLoaded = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
|
|
889
896
|
const allMemories = allLoaded.filter(({ memory }) => {
|
|
@@ -892,7 +899,7 @@ async function getBriefing(input, ctx) {
|
|
|
892
899
|
if (!input.include_stale && s === "stale") return false;
|
|
893
900
|
return true;
|
|
894
901
|
});
|
|
895
|
-
|
|
902
|
+
usage = await loadUsageIndex7(ctx.paths);
|
|
896
903
|
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
897
904
|
if (input.task && input.semantic) {
|
|
898
905
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
@@ -949,7 +956,6 @@ async function getBriefing(input, ctx) {
|
|
|
949
956
|
}
|
|
950
957
|
}
|
|
951
958
|
if (semanticHits) {
|
|
952
|
-
const byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
953
959
|
for (const hit of semanticHits) {
|
|
954
960
|
const loaded = byId.get(hit.id);
|
|
955
961
|
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
@@ -964,6 +970,17 @@ async function getBriefing(input, ctx) {
|
|
|
964
970
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
965
971
|
return sb - sa;
|
|
966
972
|
});
|
|
973
|
+
byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
974
|
+
for (const mem of ranked.slice(0, input.max_memories)) {
|
|
975
|
+
if (seen.size >= input.max_memories * 2) break;
|
|
976
|
+
const loaded = byId.get(mem.id);
|
|
977
|
+
if (!loaded) continue;
|
|
978
|
+
for (const relId of loaded.memory.frontmatter.related_ids ?? []) {
|
|
979
|
+
if (seen.has(relId)) continue;
|
|
980
|
+
const related = byId.get(relId);
|
|
981
|
+
if (related) addOrUpdate(related, "anchor", void 0, "partial");
|
|
982
|
+
}
|
|
983
|
+
}
|
|
967
984
|
memories.push(...ranked.slice(0, input.max_memories));
|
|
968
985
|
if (input.track && memories.length > 0) {
|
|
969
986
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
@@ -1004,7 +1021,6 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1004
1021
|
trimmedModules.push({ name: m.name, content: sub.text, truncated: sub.truncated });
|
|
1005
1022
|
}
|
|
1006
1023
|
}
|
|
1007
|
-
const trimmedMemoriesText = memoriesSlice.text;
|
|
1008
1024
|
const trimmedMemories = [];
|
|
1009
1025
|
if (!memoriesSlice.truncated) {
|
|
1010
1026
|
trimmedMemories.push(...memories);
|
|
@@ -1024,13 +1040,22 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1024
1040
|
}
|
|
1025
1041
|
}
|
|
1026
1042
|
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
1043
|
+
const decayWarnings = [];
|
|
1044
|
+
for (const m of trimmedMemories) {
|
|
1045
|
+
const u = getUsage5(usage, m.id);
|
|
1046
|
+
const loaded = byId.get(m.id);
|
|
1047
|
+
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1048
|
+
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
1049
|
+
}
|
|
1050
|
+
const outputMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : trimmedMemories;
|
|
1027
1051
|
return {
|
|
1028
1052
|
...input.task ? { task: input.task } : {},
|
|
1029
1053
|
search_mode: searchMode,
|
|
1030
1054
|
inferred_modules: inferred,
|
|
1031
1055
|
project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
|
|
1032
1056
|
module_contexts: trimmedModules,
|
|
1033
|
-
memories:
|
|
1057
|
+
memories: outputMemories,
|
|
1058
|
+
decay_warnings: decayWarnings,
|
|
1034
1059
|
estimated_tokens: totalTokens,
|
|
1035
1060
|
budget: {
|
|
1036
1061
|
max_tokens: input.max_tokens,
|
|
@@ -1042,6 +1067,13 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1042
1067
|
}
|
|
1043
1068
|
};
|
|
1044
1069
|
}
|
|
1070
|
+
function compactSummary(body) {
|
|
1071
|
+
for (const line of body.split("\n")) {
|
|
1072
|
+
const trimmed = line.replace(/^#+\s*/, "").trim();
|
|
1073
|
+
if (trimmed.length > 0) return trimmed.slice(0, 120);
|
|
1074
|
+
}
|
|
1075
|
+
return body.slice(0, 120);
|
|
1076
|
+
}
|
|
1045
1077
|
async function trySemanticHits(ctx, task, limit) {
|
|
1046
1078
|
let mod;
|
|
1047
1079
|
try {
|
|
@@ -1101,13 +1133,58 @@ async function codeMapTool(input, ctx) {
|
|
|
1101
1133
|
};
|
|
1102
1134
|
}
|
|
1103
1135
|
|
|
1104
|
-
// src/
|
|
1136
|
+
// src/tools/mem-diff.ts
|
|
1137
|
+
import { existsSync as existsSync16 } from "fs";
|
|
1138
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir13 } from "@hiveai/core";
|
|
1105
1139
|
import { z as z17 } from "zod";
|
|
1140
|
+
var MemDiffInputSchema = {
|
|
1141
|
+
id_a: z17.string().min(1).describe("First memory id"),
|
|
1142
|
+
id_b: z17.string().min(1).describe("Second memory id")
|
|
1143
|
+
};
|
|
1144
|
+
async function memDiff(input, ctx) {
|
|
1145
|
+
if (!existsSync16(ctx.paths.memoriesDir)) {
|
|
1146
|
+
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
1147
|
+
}
|
|
1148
|
+
const all = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1149
|
+
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
1150
|
+
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
1151
|
+
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
1152
|
+
if (!foundB) throw new Error(`No memory with id "${input.id_b}".`);
|
|
1153
|
+
const fmA = foundA.memory.frontmatter;
|
|
1154
|
+
const fmB = foundB.memory.frontmatter;
|
|
1155
|
+
const frontmatterDiff = {};
|
|
1156
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(fmA), ...Object.keys(fmB)]);
|
|
1157
|
+
for (const key of allKeys) {
|
|
1158
|
+
const va = fmA[key];
|
|
1159
|
+
const vb = fmB[key];
|
|
1160
|
+
if (JSON.stringify(va) !== JSON.stringify(vb)) {
|
|
1161
|
+
frontmatterDiff[key] = { a: va, b: vb };
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
const linesA = new Set(foundA.memory.body.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
1165
|
+
const linesB = new Set(foundB.memory.body.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
1166
|
+
const onlyA = [...linesA].filter((l) => !linesB.has(l));
|
|
1167
|
+
const onlyB = [...linesB].filter((l) => !linesA.has(l));
|
|
1168
|
+
const common = [...linesA].filter((l) => linesB.has(l)).length;
|
|
1169
|
+
return {
|
|
1170
|
+
id_a: input.id_a,
|
|
1171
|
+
id_b: input.id_b,
|
|
1172
|
+
frontmatter_diff: frontmatterDiff,
|
|
1173
|
+
body_diff: {
|
|
1174
|
+
lines_only_in_a: onlyA,
|
|
1175
|
+
lines_only_in_b: onlyB,
|
|
1176
|
+
common_lines: common
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/prompts/bootstrap-project.ts
|
|
1182
|
+
import { z as z18 } from "zod";
|
|
1106
1183
|
var BootstrapProjectArgsSchema = {
|
|
1107
|
-
module:
|
|
1184
|
+
module: z18.string().optional().describe(
|
|
1108
1185
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1109
1186
|
),
|
|
1110
|
-
focus:
|
|
1187
|
+
focus: z18.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1111
1188
|
};
|
|
1112
1189
|
var ROOT_TEMPLATE = `# Project context
|
|
1113
1190
|
|
|
@@ -1189,10 +1266,10 @@ ${template}\`\`\`
|
|
|
1189
1266
|
}
|
|
1190
1267
|
|
|
1191
1268
|
// src/prompts/post-task.ts
|
|
1192
|
-
import { z as
|
|
1269
|
+
import { z as z19 } from "zod";
|
|
1193
1270
|
var PostTaskArgsSchema = {
|
|
1194
|
-
task_summary:
|
|
1195
|
-
files_touched:
|
|
1271
|
+
task_summary: z19.string().optional().describe("One sentence describing what you just did"),
|
|
1272
|
+
files_touched: z19.array(z19.string()).optional().describe("Files you created or modified during the task")
|
|
1196
1273
|
};
|
|
1197
1274
|
function postTaskPrompt(args, ctx) {
|
|
1198
1275
|
const taskLine = args.task_summary ? `
|
|
@@ -1248,9 +1325,78 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "N
|
|
|
1248
1325
|
};
|
|
1249
1326
|
}
|
|
1250
1327
|
|
|
1328
|
+
// src/prompts/import-docs.ts
|
|
1329
|
+
import { z as z20 } from "zod";
|
|
1330
|
+
var ImportDocsArgsSchema = {
|
|
1331
|
+
content: z20.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
1332
|
+
source: z20.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
1333
|
+
scope: z20.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
1334
|
+
dry_run: z20.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
1335
|
+
};
|
|
1336
|
+
function importDocsPrompt(args, ctx) {
|
|
1337
|
+
const sourceLine = args.source ? `
|
|
1338
|
+
Source: **${args.source}**` : "";
|
|
1339
|
+
const dryRunNote = args.dry_run ? "\n> **DRY RUN** \u2014 describe what you would save but do not call any tools." : "";
|
|
1340
|
+
const text = `You are given documentation to analyze and import into the hAIve memory system.
|
|
1341
|
+
${sourceLine}
|
|
1342
|
+
Scope: **${args.scope}**
|
|
1343
|
+
Project root: \`${ctx.paths.root}\`
|
|
1344
|
+
${dryRunNote}
|
|
1345
|
+
|
|
1346
|
+
## Your task
|
|
1347
|
+
|
|
1348
|
+
Read the documentation below and extract actionable memories. For each distinct piece of knowledge:
|
|
1349
|
+
|
|
1350
|
+
1. **Identify the memory type** \u2014 which category fits best?
|
|
1351
|
+
- \`convention\` \u2014 how things are done here (naming, patterns, workflow)
|
|
1352
|
+
- \`decision\` \u2014 a choice that was made and why (tradeoffs, constraints)
|
|
1353
|
+
- \`gotcha\` \u2014 non-obvious behavior, traps, things that surprise newcomers
|
|
1354
|
+
- \`architecture\` \u2014 structural overview of a system or module
|
|
1355
|
+
- \`glossary\` \u2014 domain terms and their meaning in this project
|
|
1356
|
+
|
|
1357
|
+
2. **Determine the anchor** \u2014 which files or symbols does this knowledge apply to? List them in \`paths\`.
|
|
1358
|
+
|
|
1359
|
+
3. **Write a focused body** \u2014 one memory = one insight. Do not combine multiple unrelated facts.
|
|
1360
|
+
- Start with the key fact or rule
|
|
1361
|
+
- Add context: why it matters, when it applies
|
|
1362
|
+
- Add examples if helpful
|
|
1363
|
+
|
|
1364
|
+
4. **Call \`mem_save\`** for each memory (unless dry_run).
|
|
1365
|
+
- Set \`scope="${args.scope}"\`
|
|
1366
|
+
- Set \`slug\` to a short kebab-case identifier
|
|
1367
|
+
- Set \`paths\` to the relevant file paths (extracted from the doc if present)
|
|
1368
|
+
|
|
1369
|
+
## Rules
|
|
1370
|
+
|
|
1371
|
+
- Skip generic documentation that applies to any project (e.g., "install with npm install").
|
|
1372
|
+
- Prioritize gotchas, non-obvious decisions, and domain-specific conventions.
|
|
1373
|
+
- If the same knowledge is repeated in different sections, save it once.
|
|
1374
|
+
- Maximum 10 memories per import \u2014 select the most actionable ones.
|
|
1375
|
+
|
|
1376
|
+
## Documentation to import
|
|
1377
|
+
|
|
1378
|
+
---
|
|
1379
|
+
|
|
1380
|
+
${args.content}
|
|
1381
|
+
|
|
1382
|
+
---
|
|
1383
|
+
|
|
1384
|
+
When done, respond with: "Imported N memories: [list of IDs]" or "Nothing actionable found."
|
|
1385
|
+
`;
|
|
1386
|
+
return {
|
|
1387
|
+
description: "Import documentation as hAIve memories",
|
|
1388
|
+
messages: [
|
|
1389
|
+
{
|
|
1390
|
+
role: "user",
|
|
1391
|
+
content: { type: "text", text }
|
|
1392
|
+
}
|
|
1393
|
+
]
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1251
1397
|
// src/server.ts
|
|
1252
1398
|
var SERVER_NAME = "haive";
|
|
1253
|
-
var SERVER_VERSION = "0.2.
|
|
1399
|
+
var SERVER_VERSION = "0.2.8";
|
|
1254
1400
|
function jsonResult(data) {
|
|
1255
1401
|
return {
|
|
1256
1402
|
content: [
|
|
@@ -1363,6 +1509,12 @@ function createHaiveServer(options = {}) {
|
|
|
1363
1509
|
MemTriedInputSchema,
|
|
1364
1510
|
async (input) => jsonResult(await memTried(input, context))
|
|
1365
1511
|
);
|
|
1512
|
+
server.tool(
|
|
1513
|
+
"mem_diff",
|
|
1514
|
+
"Compare two memories side-by-side: shows frontmatter fields that differ and lines unique to each body. Useful before merging or deduplicating memories.",
|
|
1515
|
+
MemDiffInputSchema,
|
|
1516
|
+
async (input) => jsonResult(await memDiff(input, context))
|
|
1517
|
+
);
|
|
1366
1518
|
server.prompt(
|
|
1367
1519
|
"bootstrap_project",
|
|
1368
1520
|
"Instructions for the AI client to analyze the project and save the context.",
|
|
@@ -1375,6 +1527,12 @@ function createHaiveServer(options = {}) {
|
|
|
1375
1527
|
PostTaskArgsSchema,
|
|
1376
1528
|
(args) => postTaskPrompt(args, context)
|
|
1377
1529
|
);
|
|
1530
|
+
server.prompt(
|
|
1531
|
+
"import_docs",
|
|
1532
|
+
"Analyze documentation (README, ADR, wiki page, etc.) and save the actionable knowledge as hAIve memories. Pass the content and an optional source/scope.",
|
|
1533
|
+
ImportDocsArgsSchema,
|
|
1534
|
+
(args) => importDocsPrompt(args, context)
|
|
1535
|
+
);
|
|
1378
1536
|
return { server, context };
|
|
1379
1537
|
}
|
|
1380
1538
|
export {
|