@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 +139 -47
- package/dist/index.js.map +1 -1
- package/dist/server.js +138 -46
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
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
|
|
413
|
-
if (!
|
|
414
|
-
|
|
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-
|
|
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
|
|
694
|
+
loadMemoriesFromDir as loadMemoriesFromDir9,
|
|
614
695
|
loadUsageIndex as loadUsageIndex6
|
|
615
696
|
} from "@hiveai/core";
|
|
616
|
-
import { z as
|
|
697
|
+
import { z as z12 } from "zod";
|
|
617
698
|
var MemPendingInputSchema = {
|
|
618
|
-
scope:
|
|
699
|
+
scope: z12.enum(["personal", "team", "module"]).optional()
|
|
619
700
|
};
|
|
620
701
|
async function memPending(input, ctx) {
|
|
621
|
-
if (!
|
|
622
|
-
const all = await
|
|
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
|
|
654
|
-
import { existsSync as
|
|
734
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
735
|
+
import { existsSync as existsSync13 } from "fs";
|
|
655
736
|
import {
|
|
656
|
-
loadMemoriesFromDir as
|
|
657
|
-
serializeMemory as
|
|
737
|
+
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
738
|
+
serializeMemory as serializeMemory5
|
|
658
739
|
} from "@hiveai/core";
|
|
659
|
-
import { z as
|
|
740
|
+
import { z as z13 } from "zod";
|
|
660
741
|
var MemApproveInputSchema = {
|
|
661
|
-
id:
|
|
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 (!
|
|
745
|
+
if (!existsSync13(ctx.paths.memoriesDir)) {
|
|
665
746
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
666
747
|
}
|
|
667
|
-
const all = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
783
|
+
import { z as z14 } from "zod";
|
|
703
784
|
var GetBriefingInputSchema = {
|
|
704
|
-
task:
|
|
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:
|
|
708
|
-
max_tokens:
|
|
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:
|
|
712
|
-
include_project_context:
|
|
713
|
-
include_module_contexts:
|
|
714
|
-
semantic:
|
|
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:
|
|
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
|
-
|
|
723
|
-
|
|
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 &&
|
|
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 (!
|
|
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 (
|
|
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
|
|
964
|
+
import { z as z15 } from "zod";
|
|
879
965
|
var CodeMapInputSchema = {
|
|
880
|
-
file:
|
|
881
|
-
symbol:
|
|
882
|
-
max_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
|
|
994
|
+
import { z as z16 } from "zod";
|
|
909
995
|
var BootstrapProjectArgsSchema = {
|
|
910
|
-
module:
|
|
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:
|
|
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.
|
|
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
|
|
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);
|