@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 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 mod = loaded.memory.frontmatter.module;
473
- if (mod && inferred.includes(mod)) {
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(loaded.memory.frontmatter.id);
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/prompts/bootstrap-project.ts
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: z13.string().optional().describe(
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: z13.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
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.0";
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.",