@hiveai/mcp 0.2.1 → 0.2.2

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 CHANGED
@@ -85,7 +85,8 @@ var MemListInputSchema = {
85
85
  scope: z3.enum(["personal", "team", "module"]).optional(),
86
86
  type: z3.enum(["convention", "decision", "gotcha", "architecture", "glossary"]).optional(),
87
87
  module: z3.string().optional(),
88
- tag: z3.string().optional()
88
+ tag: z3.string().optional(),
89
+ include_body: z3.boolean().default(false).describe("Include full body text. Default false to save tokens \u2014 use mem_get for a single memory's full content.")
89
90
  };
90
91
  async function memList(input, ctx) {
91
92
  if (!existsSync3(ctx.paths.memoriesDir)) {
@@ -102,6 +103,7 @@ async function memList(input, ctx) {
102
103
  });
103
104
  const memories = filtered.map(({ memory, filePath }) => {
104
105
  const fm = memory.frontmatter;
106
+ const snippet = memory.body.replace(/\s+/g, " ").trim().slice(0, 120);
105
107
  return {
106
108
  id: fm.id,
107
109
  scope: fm.scope,
@@ -109,7 +111,9 @@ async function memList(input, ctx) {
109
111
  ...fm.module ? { module: fm.module } : {},
110
112
  status: fm.status,
111
113
  tags: fm.tags,
112
- file_path: filePath
114
+ snippet,
115
+ file_path: filePath,
116
+ ...input.include_body ? { body: memory.body } : {}
113
117
  };
114
118
  });
115
119
  return { memories };
@@ -335,6 +339,14 @@ async function memVerify(input, ctx) {
335
339
  const isAnchored = memory.frontmatter.anchor.paths.length > 0 || memory.frontmatter.anchor.symbols.length > 0;
336
340
  if (!isAnchored) {
337
341
  anchorless++;
342
+ results.push({
343
+ id: memory.frontmatter.id,
344
+ file_path: filePath,
345
+ stale: false,
346
+ reason: null,
347
+ status_after: memory.frontmatter.status,
348
+ skipped: true
349
+ });
338
350
  continue;
339
351
  }
340
352
  const result = await verifyAnchor(memory, { projectRoot: ctx.paths.root });
@@ -392,12 +404,14 @@ function applyVerification(mem, result) {
392
404
  }
393
405
 
394
406
  // src/tools/mem-reject.ts
407
+ import { writeFile as writeFile4 } from "fs/promises";
395
408
  import { existsSync as existsSync7 } from "fs";
396
409
  import {
397
410
  loadMemoriesFromDir as loadMemoriesFromDir4,
398
411
  loadUsageIndex as loadUsageIndex2,
399
412
  recordRejection,
400
- saveUsageIndex
413
+ saveUsageIndex,
414
+ serializeMemory as serializeMemory3
401
415
  } from "@hiveai/core";
402
416
  import { z as z7 } from "zod";
403
417
  var MemRejectInputSchema = {
@@ -409,16 +423,23 @@ async function memReject(input, ctx) {
409
423
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
410
424
  }
411
425
  const memories = await loadMemoriesFromDir4(ctx.paths.memoriesDir);
412
- const exists = memories.some((m) => m.memory.frontmatter.id === input.id);
413
- if (!exists) {
414
- throw new Error(`No memory with id "${input.id}".`);
415
- }
426
+ const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
427
+ if (!loaded) throw new Error(`No memory with id "${input.id}".`);
428
+ await writeFile4(
429
+ loaded.filePath,
430
+ serializeMemory3({
431
+ frontmatter: { ...loaded.memory.frontmatter, status: "rejected" },
432
+ body: loaded.memory.body
433
+ }),
434
+ "utf8"
435
+ );
416
436
  const idx = await loadUsageIndex2(ctx.paths);
417
437
  recordRejection(idx, input.id, input.reason ?? null);
418
438
  await saveUsageIndex(ctx.paths, idx);
419
439
  const u = idx.by_id[input.id];
420
440
  return {
421
441
  id: input.id,
442
+ status: "rejected",
422
443
  rejected_count: u?.rejected_count ?? 0,
423
444
  last_rejected_at: u?.last_rejected_at ?? null,
424
445
  rejection_reason: u?.rejection_reason ?? null
@@ -606,20 +627,80 @@ async function memDelete(input, ctx) {
606
627
  return { id: input.id, deleted_file: found.filePath, usage_removed: usageRemoved };
607
628
  }
608
629
 
609
- // src/tools/mem-pending.ts
630
+ // src/tools/mem-update.ts
631
+ import { writeFile as writeFile5 } from "fs/promises";
610
632
  import { existsSync as existsSync11 } from "fs";
633
+ import { loadMemoriesFromDir as loadMemoriesFromDir8, serializeMemory as serializeMemory4 } from "@hiveai/core";
634
+ import { z as z11 } from "zod";
635
+ var MemUpdateInputSchema = {
636
+ id: z11.string().min(1).describe("Id of the memory to update"),
637
+ body: z11.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
638
+ tags: z11.array(z11.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
639
+ paths: z11.array(z11.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
640
+ symbols: z11.array(z11.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
641
+ commit: z11.string().optional().describe("New anchor commit SHA"),
642
+ domain: z11.string().optional().describe("New domain label"),
643
+ author: z11.string().optional().describe("New author handle or email")
644
+ };
645
+ async function memUpdate(input, ctx) {
646
+ if (!existsSync11(ctx.paths.memoriesDir)) {
647
+ throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
648
+ }
649
+ const memories = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
650
+ const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
651
+ if (!loaded) throw new Error(`No memory with id "${input.id}".`);
652
+ const { frontmatter, body } = loaded.memory;
653
+ const updated_fields = [];
654
+ const newAnchor = { ...frontmatter.anchor };
655
+ if (input.paths !== void 0) {
656
+ newAnchor.paths = input.paths;
657
+ updated_fields.push("anchor.paths");
658
+ }
659
+ if (input.symbols !== void 0) {
660
+ newAnchor.symbols = input.symbols;
661
+ updated_fields.push("anchor.symbols");
662
+ }
663
+ if (input.commit !== void 0) {
664
+ newAnchor.commit = input.commit;
665
+ updated_fields.push("anchor.commit");
666
+ }
667
+ const newFrontmatter = {
668
+ ...frontmatter,
669
+ anchor: newAnchor,
670
+ ...input.tags !== void 0 ? { tags: input.tags } : {},
671
+ ...input.domain !== void 0 ? { domain: input.domain } : {},
672
+ ...input.author !== void 0 ? { author: input.author } : {}
673
+ };
674
+ if (input.tags !== void 0) updated_fields.push("tags");
675
+ if (input.domain !== void 0) updated_fields.push("domain");
676
+ if (input.author !== void 0) updated_fields.push("author");
677
+ const newBody = input.body !== void 0 ? input.body : body;
678
+ if (input.body !== void 0) updated_fields.push("body");
679
+ if (updated_fields.length === 0) {
680
+ throw new Error("No fields to update \u2014 provide at least one of: body, tags, paths, symbols, commit, domain, author.");
681
+ }
682
+ await writeFile5(
683
+ loaded.filePath,
684
+ serializeMemory4({ frontmatter: newFrontmatter, body: newBody }),
685
+ "utf8"
686
+ );
687
+ return { id: input.id, file_path: loaded.filePath, updated_fields };
688
+ }
689
+
690
+ // src/tools/mem-pending.ts
691
+ import { existsSync as existsSync12 } from "fs";
611
692
  import {
612
693
  getUsage as getUsage4,
613
- loadMemoriesFromDir as loadMemoriesFromDir8,
694
+ loadMemoriesFromDir as loadMemoriesFromDir9,
614
695
  loadUsageIndex as loadUsageIndex6
615
696
  } from "@hiveai/core";
616
- import { z as z11 } from "zod";
697
+ import { z as z12 } from "zod";
617
698
  var MemPendingInputSchema = {
618
- scope: z11.enum(["personal", "team", "module"]).optional()
699
+ scope: z12.enum(["personal", "team", "module"]).optional()
619
700
  };
620
701
  async function memPending(input, ctx) {
621
- if (!existsSync11(ctx.paths.memoriesDir)) return { pending: [] };
622
- const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
702
+ if (!existsSync12(ctx.paths.memoriesDir)) return { pending: [] };
703
+ const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
623
704
  const usage = await loadUsageIndex6(ctx.paths);
624
705
  const now = Date.now();
625
706
  const proposed = all.filter(({ memory }) => {
@@ -650,21 +731,21 @@ async function memPending(input, ctx) {
650
731
  }
651
732
 
652
733
  // src/tools/mem-approve.ts
653
- import { writeFile as writeFile4 } from "fs/promises";
654
- import { existsSync as existsSync12 } from "fs";
734
+ import { writeFile as writeFile6 } from "fs/promises";
735
+ import { existsSync as existsSync13 } from "fs";
655
736
  import {
656
- loadMemoriesFromDir as loadMemoriesFromDir9,
657
- serializeMemory as serializeMemory3
737
+ loadMemoriesFromDir as loadMemoriesFromDir10,
738
+ serializeMemory as serializeMemory5
658
739
  } from "@hiveai/core";
659
- import { z as z12 } from "zod";
740
+ import { z as z13 } from "zod";
660
741
  var MemApproveInputSchema = {
661
- id: z12.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
742
+ id: z13.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
662
743
  };
663
744
  async function memApprove(input, ctx) {
664
- if (!existsSync12(ctx.paths.memoriesDir)) {
745
+ if (!existsSync13(ctx.paths.memoriesDir)) {
665
746
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
666
747
  }
667
- const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
748
+ const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
668
749
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
669
750
  if (!found) throw new Error(`No memory with id "${input.id}".`);
670
751
  const previous = found.memory.frontmatter.status;
@@ -672,7 +753,7 @@ async function memApprove(input, ctx) {
672
753
  frontmatter: { ...found.memory.frontmatter, status: "validated" },
673
754
  body: found.memory.body
674
755
  };
675
- await writeFile4(found.filePath, serializeMemory3(next), "utf8");
756
+ await writeFile6(found.filePath, serializeMemory5(next), "utf8");
676
757
  return {
677
758
  id: input.id,
678
759
  previous_status: previous,
@@ -683,7 +764,7 @@ async function memApprove(input, ctx) {
683
764
 
684
765
  // src/tools/get-briefing.ts
685
766
  import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
686
- import { existsSync as existsSync13 } from "fs";
767
+ import { existsSync as existsSync14 } from "fs";
687
768
  import path5 from "path";
688
769
  import {
689
770
  allocateBudget,
@@ -692,37 +773,41 @@ import {
692
773
  getUsage as getUsage5,
693
774
  inferModulesFromPaths as inferModulesFromPaths2,
694
775
  literalMatchesAllTokens as literalMatchesAllTokens2,
695
- loadMemoriesFromDir as loadMemoriesFromDir10,
776
+ loadMemoriesFromDir as loadMemoriesFromDir11,
696
777
  loadUsageIndex as loadUsageIndex7,
697
778
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
698
779
  tokenizeQuery as tokenizeQuery2,
699
780
  trackReads as trackReads3,
700
781
  truncateToTokens
701
782
  } from "@hiveai/core";
702
- import { z as z13 } from "zod";
783
+ import { z as z14 } from "zod";
703
784
  var GetBriefingInputSchema = {
704
- task: z13.string().optional().describe(
785
+ task: z14.string().optional().describe(
705
786
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
706
787
  ),
707
- files: z13.array(z13.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
708
- max_tokens: z13.number().int().positive().default(8e3).describe(
788
+ files: z14.array(z14.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
789
+ max_tokens: z14.number().int().positive().default(8e3).describe(
709
790
  "Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
710
791
  ),
711
- max_memories: z13.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
712
- include_project_context: z13.boolean().default(true),
713
- include_module_contexts: z13.boolean().default(true),
714
- semantic: z13.boolean().default(true).describe(
792
+ max_memories: z14.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
793
+ include_project_context: z14.boolean().default(true),
794
+ include_module_contexts: z14.boolean().default(true),
795
+ semantic: z14.boolean().default(true).describe(
715
796
  "Use semantic ranking when a task is provided (requires `haive embeddings index`)."
716
797
  ),
717
- track: z13.boolean().default(true).describe("Increment read_count on returned memories")
798
+ track: z14.boolean().default(true).describe("Increment read_count on returned memories")
718
799
  };
719
800
  async function getBriefing(input, ctx) {
720
801
  const inferred = inferModulesFromPaths2(input.files);
721
802
  const memories = [];
722
- if (existsSync13(ctx.paths.memoriesDir)) {
723
- const allMemories = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
803
+ let searchMode = "literal";
804
+ if (existsSync14(ctx.paths.memoriesDir)) {
805
+ const allMemories = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
724
806
  const usage = await loadUsageIndex7(ctx.paths);
725
807
  const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
808
+ if (input.task && input.semantic) {
809
+ searchMode = semanticHits ? "semantic" : "literal_fallback";
810
+ }
726
811
  const seen = /* @__PURE__ */ new Map();
727
812
  const addOrUpdate = (loaded, reason, score) => {
728
813
  const fm = loaded.memory.frontmatter;
@@ -788,7 +873,7 @@ async function getBriefing(input, ctx) {
788
873
  await trackReads3(ctx.paths, memories.map((m) => m.id));
789
874
  }
790
875
  }
791
- const projectContext = input.include_project_context && existsSync13(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
876
+ const projectContext = input.include_project_context && existsSync14(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
792
877
  const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
793
878
  const memoriesText = memories.map((m) => `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})
794
879
  ${m.body.trim()}`).join("\n\n---\n\n");
@@ -830,6 +915,7 @@ ${m.content}`).join("\n\n---\n\n"),
830
915
  const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
831
916
  return {
832
917
  ...input.task ? { task: input.task } : {},
918
+ search_mode: searchMode,
833
919
  inferred_modules: inferred,
834
920
  project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
835
921
  module_contexts: trimmedModules,
@@ -858,7 +944,7 @@ async function trySemanticHits(ctx, task, limit) {
858
944
  }
859
945
  async function loadModuleContexts2(ctx, modules) {
860
946
  if (modules.length === 0) return [];
861
- if (!existsSync13(ctx.paths.modulesContextDir)) return [];
947
+ if (!existsSync14(ctx.paths.modulesContextDir)) return [];
862
948
  const available = new Set(
863
949
  (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
864
950
  );
@@ -866,7 +952,7 @@ async function loadModuleContexts2(ctx, modules) {
866
952
  for (const m of modules) {
867
953
  if (!available.has(m)) continue;
868
954
  const file = path5.join(ctx.paths.modulesContextDir, m, "context.md");
869
- if (existsSync13(file)) {
955
+ if (existsSync14(file)) {
870
956
  out.push({ name: m, content: await readFile3(file, "utf8") });
871
957
  }
872
958
  }
@@ -875,11 +961,11 @@ async function loadModuleContexts2(ctx, modules) {
875
961
 
876
962
  // src/tools/code-map.ts
877
963
  import { loadCodeMap, queryCodeMap } from "@hiveai/core";
878
- import { z as z14 } from "zod";
964
+ import { z as z15 } from "zod";
879
965
  var CodeMapInputSchema = {
880
- file: z14.string().optional().describe("Filter to files whose path contains this substring"),
881
- symbol: z14.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
882
- max_files: z14.number().int().positive().default(40).describe("Cap on returned files")
966
+ file: z15.string().optional().describe("Filter to files whose path contains this substring"),
967
+ symbol: z15.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
968
+ max_files: z15.number().int().positive().default(40).describe("Cap on returned files")
883
969
  };
884
970
  async function codeMapTool(input, ctx) {
885
971
  const map = await loadCodeMap(ctx.paths);
@@ -905,12 +991,12 @@ async function codeMapTool(input, ctx) {
905
991
  }
906
992
 
907
993
  // src/prompts/bootstrap-project.ts
908
- import { z as z15 } from "zod";
994
+ import { z as z16 } from "zod";
909
995
  var BootstrapProjectArgsSchema = {
910
- module: z15.string().optional().describe(
996
+ module: z16.string().optional().describe(
911
997
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
912
998
  ),
913
- focus: z15.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
999
+ focus: z16.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
914
1000
  };
915
1001
  var ROOT_TEMPLATE = `# Project context
916
1002
 
@@ -993,7 +1079,7 @@ ${template}\`\`\`
993
1079
 
994
1080
  // src/server.ts
995
1081
  var SERVER_NAME = "haive";
996
- var SERVER_VERSION = "0.2.1";
1082
+ var SERVER_VERSION = "0.2.2";
997
1083
  function jsonResult(data) {
998
1084
  return {
999
1085
  content: [
@@ -1082,6 +1168,12 @@ function createHaiveServer(options = {}) {
1082
1168
  MemDeleteInputSchema,
1083
1169
  async (input) => jsonResult(await memDelete(input, context))
1084
1170
  );
1171
+ server.tool(
1172
+ "mem_update",
1173
+ "Update the body, tags, or anchor of an existing memory without changing its id or losing usage history.",
1174
+ MemUpdateInputSchema,
1175
+ async (input) => jsonResult(await memUpdate(input, context))
1176
+ );
1085
1177
  server.tool(
1086
1178
  "mem_pending",
1087
1179
  "List 'proposed' memories awaiting review, sorted by reads (most-read first).",
@@ -1120,7 +1212,7 @@ async function main() {
1120
1212
  const { root } = parseArgs(process.argv);
1121
1213
  const { server, context } = createHaiveServer({ root });
1122
1214
  console.error(
1123
- `[haive-mcp] starting server v0.1.0 (project root: ${context.paths.root})`
1215
+ `[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
1124
1216
  );
1125
1217
  const transport = new StdioServerTransport();
1126
1218
  await server.connect(transport);