@hiveai/mcp 0.1.1 → 0.2.1
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 +243 -7
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +243 -7
- package/dist/server.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -469,10 +469,11 @@ async function memForFiles(input, ctx) {
|
|
|
469
469
|
}
|
|
470
470
|
for (const loaded of all) {
|
|
471
471
|
if (seen.has(loaded.memory.frontmatter.id)) continue;
|
|
472
|
-
const
|
|
473
|
-
|
|
472
|
+
const fm = loaded.memory.frontmatter;
|
|
473
|
+
const moduleHit = fm.module && inferred.includes(fm.module) || fm.tags.some((t) => inferred.includes(t));
|
|
474
|
+
if (moduleHit) {
|
|
474
475
|
byModule.push(toMatch(loaded, "module", usage));
|
|
475
|
-
seen.add(
|
|
476
|
+
seen.add(fm.id);
|
|
476
477
|
}
|
|
477
478
|
}
|
|
478
479
|
for (const loaded of all) {
|
|
@@ -680,13 +681,236 @@ async function memApprove(input, ctx) {
|
|
|
680
681
|
};
|
|
681
682
|
}
|
|
682
683
|
|
|
683
|
-
// src/
|
|
684
|
+
// src/tools/get-briefing.ts
|
|
685
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
686
|
+
import { existsSync as existsSync13 } from "fs";
|
|
687
|
+
import path5 from "path";
|
|
688
|
+
import {
|
|
689
|
+
allocateBudget,
|
|
690
|
+
deriveConfidence as deriveConfidence4,
|
|
691
|
+
estimateTokens,
|
|
692
|
+
getUsage as getUsage5,
|
|
693
|
+
inferModulesFromPaths as inferModulesFromPaths2,
|
|
694
|
+
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
695
|
+
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
696
|
+
loadUsageIndex as loadUsageIndex7,
|
|
697
|
+
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
698
|
+
tokenizeQuery as tokenizeQuery2,
|
|
699
|
+
trackReads as trackReads3,
|
|
700
|
+
truncateToTokens
|
|
701
|
+
} from "@hiveai/core";
|
|
684
702
|
import { z as z13 } from "zod";
|
|
703
|
+
var GetBriefingInputSchema = {
|
|
704
|
+
task: z13.string().optional().describe(
|
|
705
|
+
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
706
|
+
),
|
|
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(
|
|
709
|
+
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
710
|
+
),
|
|
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(
|
|
715
|
+
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
716
|
+
),
|
|
717
|
+
track: z13.boolean().default(true).describe("Increment read_count on returned memories")
|
|
718
|
+
};
|
|
719
|
+
async function getBriefing(input, ctx) {
|
|
720
|
+
const inferred = inferModulesFromPaths2(input.files);
|
|
721
|
+
const memories = [];
|
|
722
|
+
if (existsSync13(ctx.paths.memoriesDir)) {
|
|
723
|
+
const allMemories = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
|
|
724
|
+
const usage = await loadUsageIndex7(ctx.paths);
|
|
725
|
+
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
726
|
+
const seen = /* @__PURE__ */ new Map();
|
|
727
|
+
const addOrUpdate = (loaded, reason, score) => {
|
|
728
|
+
const fm = loaded.memory.frontmatter;
|
|
729
|
+
const existing = seen.get(fm.id);
|
|
730
|
+
if (existing) {
|
|
731
|
+
if (!existing.reasons.includes(reason)) existing.reasons.push(reason);
|
|
732
|
+
if (score !== void 0 && (existing.semantic_score ?? 0) < score) {
|
|
733
|
+
existing.semantic_score = score;
|
|
734
|
+
}
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const u = getUsage5(usage, fm.id);
|
|
738
|
+
seen.set(fm.id, {
|
|
739
|
+
id: fm.id,
|
|
740
|
+
scope: fm.scope,
|
|
741
|
+
type: fm.type,
|
|
742
|
+
...fm.module ? { module: fm.module } : {},
|
|
743
|
+
tags: fm.tags,
|
|
744
|
+
status: fm.status,
|
|
745
|
+
confidence: deriveConfidence4(fm, u),
|
|
746
|
+
read_count: u.read_count,
|
|
747
|
+
reasons: [reason],
|
|
748
|
+
...score !== void 0 ? { semantic_score: score } : {},
|
|
749
|
+
body: loaded.memory.body,
|
|
750
|
+
file_path: loaded.filePath
|
|
751
|
+
});
|
|
752
|
+
};
|
|
753
|
+
if (input.files.length > 0) {
|
|
754
|
+
for (const loaded of allMemories) {
|
|
755
|
+
if (memoryMatchesAnchorPaths2(loaded.memory, input.files)) addOrUpdate(loaded, "anchor");
|
|
756
|
+
}
|
|
757
|
+
for (const loaded of allMemories) {
|
|
758
|
+
const fm = loaded.memory.frontmatter;
|
|
759
|
+
if (fm.module && inferred.includes(fm.module)) addOrUpdate(loaded, "module");
|
|
760
|
+
if (fm.domain && inferred.includes(fm.domain)) addOrUpdate(loaded, "domain");
|
|
761
|
+
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module");
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (input.task) {
|
|
765
|
+
const tokens = tokenizeQuery2(input.task);
|
|
766
|
+
for (const loaded of allMemories) {
|
|
767
|
+
if (literalMatchesAllTokens2(loaded.memory, tokens)) {
|
|
768
|
+
addOrUpdate(loaded, "semantic");
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (semanticHits) {
|
|
772
|
+
const byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
773
|
+
for (const hit of semanticHits) {
|
|
774
|
+
const loaded = byId.get(hit.id);
|
|
775
|
+
if (loaded) addOrUpdate(loaded, "semantic", hit.score);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const ranked = [...seen.values()].sort((a, b) => {
|
|
780
|
+
const reasonScore = (m) => (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
781
|
+
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
782
|
+
const sa = reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
783
|
+
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
784
|
+
return sb - sa;
|
|
785
|
+
});
|
|
786
|
+
memories.push(...ranked.slice(0, input.max_memories));
|
|
787
|
+
if (input.track && memories.length > 0) {
|
|
788
|
+
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const projectContext = input.include_project_context && existsSync13(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
792
|
+
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
793
|
+
const memoriesText = memories.map((m) => `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})
|
|
794
|
+
${m.body.trim()}`).join("\n\n---\n\n");
|
|
795
|
+
const slices = allocateBudget(
|
|
796
|
+
[
|
|
797
|
+
{ key: "project", text: projectContext, weight: 3, mode: "head" },
|
|
798
|
+
{
|
|
799
|
+
key: "modules",
|
|
800
|
+
text: moduleContents.map((m) => `## ${m.name}
|
|
801
|
+
${m.content}`).join("\n\n---\n\n"),
|
|
802
|
+
weight: 3,
|
|
803
|
+
mode: "head"
|
|
804
|
+
},
|
|
805
|
+
{ key: "memories", text: memoriesText, weight: 4, mode: "head" }
|
|
806
|
+
],
|
|
807
|
+
input.max_tokens
|
|
808
|
+
);
|
|
809
|
+
const projectSlice = slices.find((s) => s.key === "project");
|
|
810
|
+
const modulesSlice = slices.find((s) => s.key === "modules");
|
|
811
|
+
const memoriesSlice = slices.find((s) => s.key === "memories");
|
|
812
|
+
const trimmedModules = [];
|
|
813
|
+
if (modulesSlice.text.length > 0 && moduleContents.length > 0) {
|
|
814
|
+
const subSlices = allocateBudget(
|
|
815
|
+
moduleContents.map((m) => ({ key: m.name, text: m.content, weight: 1, mode: "head" })),
|
|
816
|
+
modulesSlice.allocatedTokens
|
|
817
|
+
);
|
|
818
|
+
for (const m of moduleContents) {
|
|
819
|
+
const sub = subSlices.find((s) => s.key === m.name);
|
|
820
|
+
trimmedModules.push({ name: m.name, content: sub.text, truncated: sub.truncated });
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
const trimmedMemoriesText = memoriesSlice.text;
|
|
824
|
+
const trimmedMemories = memories.map((m) => {
|
|
825
|
+
if (!memoriesSlice.truncated) return m;
|
|
826
|
+
const tokensPer = Math.floor(memoriesSlice.allocatedTokens / Math.max(1, memories.length));
|
|
827
|
+
const t = truncateToTokens(m.body, { maxTokens: tokensPer, mode: "head" });
|
|
828
|
+
return { ...m, body: t.text };
|
|
829
|
+
});
|
|
830
|
+
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
831
|
+
return {
|
|
832
|
+
...input.task ? { task: input.task } : {},
|
|
833
|
+
inferred_modules: inferred,
|
|
834
|
+
project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
|
|
835
|
+
module_contexts: trimmedModules,
|
|
836
|
+
memories: trimmedMemories,
|
|
837
|
+
estimated_tokens: totalTokens,
|
|
838
|
+
budget: {
|
|
839
|
+
max_tokens: input.max_tokens,
|
|
840
|
+
spent: {
|
|
841
|
+
project: projectSlice.estimatedTokens,
|
|
842
|
+
modules: modulesSlice.estimatedTokens,
|
|
843
|
+
memories: memoriesSlice.estimatedTokens
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
async function trySemanticHits(ctx, task, limit) {
|
|
849
|
+
let mod;
|
|
850
|
+
try {
|
|
851
|
+
mod = await import("@hiveai/embeddings");
|
|
852
|
+
} catch {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
const result = await mod.semanticSearch(ctx.paths, task, { limit });
|
|
856
|
+
if (!result) return null;
|
|
857
|
+
return result.hits.map((h) => ({ id: h.id, score: h.score }));
|
|
858
|
+
}
|
|
859
|
+
async function loadModuleContexts2(ctx, modules) {
|
|
860
|
+
if (modules.length === 0) return [];
|
|
861
|
+
if (!existsSync13(ctx.paths.modulesContextDir)) return [];
|
|
862
|
+
const available = new Set(
|
|
863
|
+
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
864
|
+
);
|
|
865
|
+
const out = [];
|
|
866
|
+
for (const m of modules) {
|
|
867
|
+
if (!available.has(m)) continue;
|
|
868
|
+
const file = path5.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
869
|
+
if (existsSync13(file)) {
|
|
870
|
+
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return out;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// src/tools/code-map.ts
|
|
877
|
+
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
878
|
+
import { z as z14 } from "zod";
|
|
879
|
+
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")
|
|
883
|
+
};
|
|
884
|
+
async function codeMapTool(input, ctx) {
|
|
885
|
+
const map = await loadCodeMap(ctx.paths);
|
|
886
|
+
if (!map) {
|
|
887
|
+
return {
|
|
888
|
+
available: false,
|
|
889
|
+
files: [],
|
|
890
|
+
notice: "No code map found. Run `haive index code` to generate `.ai/code-map.json`."
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
const { files } = queryCodeMap(map, { file: input.file, symbol: input.symbol });
|
|
894
|
+
return {
|
|
895
|
+
available: true,
|
|
896
|
+
generated_at: map.generated_at,
|
|
897
|
+
total_files: Object.keys(map.files).length,
|
|
898
|
+
files: files.slice(0, input.max_files).map((f) => ({
|
|
899
|
+
path: f.path,
|
|
900
|
+
...f.entry.summary ? { summary: f.entry.summary } : {},
|
|
901
|
+
loc: f.entry.loc,
|
|
902
|
+
exports: f.entry.exports
|
|
903
|
+
}))
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/prompts/bootstrap-project.ts
|
|
908
|
+
import { z as z15 } from "zod";
|
|
685
909
|
var BootstrapProjectArgsSchema = {
|
|
686
|
-
module:
|
|
910
|
+
module: z15.string().optional().describe(
|
|
687
911
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
688
912
|
),
|
|
689
|
-
focus:
|
|
913
|
+
focus: z15.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
690
914
|
};
|
|
691
915
|
var ROOT_TEMPLATE = `# Project context
|
|
692
916
|
|
|
@@ -769,7 +993,7 @@ ${template}\`\`\`
|
|
|
769
993
|
|
|
770
994
|
// src/server.ts
|
|
771
995
|
var SERVER_NAME = "haive";
|
|
772
|
-
var SERVER_VERSION = "0.1
|
|
996
|
+
var SERVER_VERSION = "0.2.1";
|
|
773
997
|
function jsonResult(data) {
|
|
774
998
|
return {
|
|
775
999
|
content: [
|
|
@@ -810,6 +1034,18 @@ function createHaiveServer(options = {}) {
|
|
|
810
1034
|
GetProjectContextInputSchema,
|
|
811
1035
|
async (input) => jsonResult(await getProjectContext(input, context))
|
|
812
1036
|
);
|
|
1037
|
+
server.tool(
|
|
1038
|
+
"get_briefing",
|
|
1039
|
+
"One-shot onboarding: returns project context + module contexts + ranked relevant memories under a token budget. Replaces 4\u20135 separate calls when an agent starts a task.",
|
|
1040
|
+
GetBriefingInputSchema,
|
|
1041
|
+
async (input) => jsonResult(await getBriefing(input, context))
|
|
1042
|
+
);
|
|
1043
|
+
server.tool(
|
|
1044
|
+
"code_map",
|
|
1045
|
+
"Browse the project's pre-computed code map (file \u2192 exports + 1-line description) instead of grepping. Requires `haive index code`.",
|
|
1046
|
+
CodeMapInputSchema,
|
|
1047
|
+
async (input) => jsonResult(await codeMapTool(input, context))
|
|
1048
|
+
);
|
|
813
1049
|
server.tool(
|
|
814
1050
|
"bootstrap_project_save",
|
|
815
1051
|
"Persist a project (or module) context document analyzed by the AI client.",
|