@hiveai/mcp 0.2.7 → 0.2.9

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/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
- const usage = await loadUsageIndex7(ctx.paths);
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: trimmedMemories,
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/prompts/bootstrap-project.ts
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: z17.string().optional().describe(
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: z17.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
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 z18 } from "zod";
1269
+ import { z as z19 } from "zod";
1193
1270
  var PostTaskArgsSchema = {
1194
- task_summary: z18.string().optional().describe("One sentence describing what you just did"),
1195
- files_touched: z18.array(z18.string()).optional().describe("Files you created or modified during the task")
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.7";
1399
+ var SERVER_VERSION = "0.2.9";
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 {