@hiveai/mcp 0.10.8 → 0.11.0

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
@@ -151,6 +151,13 @@ var MemSaveInputSchema = {
151
151
  commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)"),
152
152
  topic: z4.string().optional().describe(
153
153
  "Stable key for this memory. If a memory with the same topic already exists in this scope, it is updated in-place (revision_count++). Use for knowledge that evolves over time."
154
+ ),
155
+ activation: z4.object({
156
+ keywords: z4.array(z4.string()).default([]),
157
+ globs: z4.array(z4.string()).default([]),
158
+ always: z4.boolean().default(false)
159
+ }).optional().describe(
160
+ "Only for type='skill'. Progressive-disclosure triggers: the skill is surfaced ONLY when a keyword matches the task or a glob matches the edited files (or always=true). Omit to keep the skill always-eligible."
154
161
  )
155
162
  };
156
163
  function bodyHash(body) {
@@ -269,7 +276,8 @@ async function memSave(input, ctx) {
269
276
  commit: input.commit,
270
277
  topic: input.topic,
271
278
  status: haiveConfig.defaultStatus === "validated" ? "validated" : void 0,
272
- sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0
279
+ sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0,
280
+ activation: input.type === "skill" ? input.activation : void 0
273
281
  });
274
282
  const file = memoryFilePath(
275
283
  ctx.paths,
@@ -658,28 +666,78 @@ async function memReject(input, ctx) {
658
666
  };
659
667
  }
660
668
 
669
+ // src/tools/mem-feedback.ts
670
+ import {
671
+ computeImpact,
672
+ getUsage as getUsage2,
673
+ loadMemoriesFromDir as loadMemoriesFromDir6,
674
+ loadUsageIndex as loadUsageIndex3,
675
+ recordApplied,
676
+ recordRejection as recordRejection2,
677
+ saveUsageIndex as saveUsageIndex2
678
+ } from "@hiveai/core";
679
+ import { existsSync as existsSync8 } from "fs";
680
+ import { z as z8 } from "zod";
681
+ var MemFeedbackInputSchema = {
682
+ id: z8.string().min(1).describe("Full memory id the feedback is about"),
683
+ outcome: z8.enum(["applied", "rejected"]).describe(
684
+ "'applied' = this memory changed what you did (strong positive utility signal); 'rejected' = it was wrong/outdated/unhelpful (negative signal, blocks auto-promotion)."
685
+ ),
686
+ reason: z8.string().optional().describe("Why it was rejected (stored on the memory's usage record). Only used for outcome='rejected'.")
687
+ };
688
+ async function memFeedback(input, ctx) {
689
+ if (!existsSync8(ctx.paths.memoriesDir)) {
690
+ return { ok: false, id: input.id, error: "No .ai/memories \u2014 run `haive init` first." };
691
+ }
692
+ const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
693
+ const target = all.find((m) => m.memory.frontmatter.id === input.id);
694
+ if (!target) {
695
+ return { ok: false, id: input.id, error: `No memory with id '${input.id}'.` };
696
+ }
697
+ const index = await loadUsageIndex3(ctx.paths);
698
+ if (input.outcome === "applied") {
699
+ recordApplied(index, input.id);
700
+ } else {
701
+ recordRejection2(index, input.id, input.reason ?? null);
702
+ }
703
+ await saveUsageIndex2(ctx.paths, index);
704
+ const usage = getUsage2(index, input.id);
705
+ const impact = computeImpact(target.memory.frontmatter, usage);
706
+ return {
707
+ ok: true,
708
+ id: input.id,
709
+ outcome: input.outcome,
710
+ usage: {
711
+ read_count: usage.read_count,
712
+ applied_count: usage.applied_count,
713
+ rejected_count: usage.rejected_count
714
+ },
715
+ impact: { score: impact.score, tier: impact.tier, signals: impact.signals }
716
+ };
717
+ }
718
+
661
719
  // src/tools/mem-for-files.ts
662
720
  import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
663
- import { existsSync as existsSync8 } from "fs";
721
+ import { existsSync as existsSync9 } from "fs";
664
722
  import path4 from "path";
665
723
  import {
666
724
  deriveConfidence as deriveConfidence2,
667
- getUsage as getUsage2,
725
+ getUsage as getUsage3,
668
726
  inferModulesFromPaths,
669
- loadMemoriesFromDir as loadMemoriesFromDir6,
670
- loadUsageIndex as loadUsageIndex3,
727
+ loadMemoriesFromDir as loadMemoriesFromDir7,
728
+ loadUsageIndex as loadUsageIndex4,
671
729
  memoryMatchesAnchorPaths,
672
730
  trackReads as trackReads2
673
731
  } from "@hiveai/core";
674
- import { z as z8 } from "zod";
732
+ import { z as z9 } from "zod";
675
733
  var MemForFilesInputSchema = {
676
- files: z8.array(z8.string()).min(1).describe("Project-relative file paths the agent is currently working on"),
677
- include_module_contexts: z8.boolean().default(true).describe("Inline the matching .ai/modules/<name>/context.md contents"),
678
- track: z8.boolean().default(true).describe("Increment read_count on returned memories")
734
+ files: z9.array(z9.string()).min(1).describe("Project-relative file paths the agent is currently working on"),
735
+ include_module_contexts: z9.boolean().default(true).describe("Inline the matching .ai/modules/<name>/context.md contents"),
736
+ track: z9.boolean().default(true).describe("Increment read_count on returned memories")
679
737
  };
680
738
  async function memForFiles(input, ctx) {
681
739
  const inferred = inferModulesFromPaths(input.files);
682
- if (!existsSync8(ctx.paths.memoriesDir)) {
740
+ if (!existsSync9(ctx.paths.memoriesDir)) {
683
741
  return {
684
742
  inferred_modules: inferred,
685
743
  by_anchor: [],
@@ -688,8 +746,8 @@ async function memForFiles(input, ctx) {
688
746
  module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
689
747
  };
690
748
  }
691
- const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
692
- const usage = await loadUsageIndex3(ctx.paths);
749
+ const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
750
+ const usage = await loadUsageIndex4(ctx.paths);
693
751
  const seen = /* @__PURE__ */ new Set();
694
752
  const byAnchor = [];
695
753
  const byModule = [];
@@ -737,7 +795,7 @@ async function memForFiles(input, ctx) {
737
795
  }
738
796
  function toMatch(loaded, reason, usage) {
739
797
  const fm = loaded.memory.frontmatter;
740
- const u = getUsage2(usage, fm.id);
798
+ const u = getUsage3(usage, fm.id);
741
799
  return {
742
800
  id: fm.id,
743
801
  scope: fm.scope,
@@ -801,7 +859,7 @@ function extractPathSegments(files) {
801
859
  }
802
860
  async function loadModuleContexts(ctx, modules, enabled) {
803
861
  if (!enabled || modules.length === 0) return [];
804
- if (!existsSync8(ctx.paths.modulesContextDir)) return [];
862
+ if (!existsSync9(ctx.paths.modulesContextDir)) return [];
805
863
  const available = new Set(
806
864
  (await readdir2(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
807
865
  );
@@ -809,7 +867,7 @@ async function loadModuleContexts(ctx, modules, enabled) {
809
867
  for (const m of modules) {
810
868
  if (!available.has(m)) continue;
811
869
  const file = path4.join(ctx.paths.modulesContextDir, m, "context.md");
812
- if (existsSync8(file)) {
870
+ if (existsSync9(file)) {
813
871
  out.push({ name: m, content: await readFile2(file, "utf8") });
814
872
  }
815
873
  }
@@ -817,26 +875,26 @@ async function loadModuleContexts(ctx, modules, enabled) {
817
875
  }
818
876
 
819
877
  // src/tools/mem-get.ts
820
- import { existsSync as existsSync9 } from "fs";
878
+ import { existsSync as existsSync10 } from "fs";
821
879
  import {
822
880
  deriveConfidence as deriveConfidence3,
823
- getUsage as getUsage3,
824
- loadMemoriesFromDir as loadMemoriesFromDir7,
825
- loadUsageIndex as loadUsageIndex4
881
+ getUsage as getUsage4,
882
+ loadMemoriesFromDir as loadMemoriesFromDir8,
883
+ loadUsageIndex as loadUsageIndex5
826
884
  } from "@hiveai/core";
827
- import { z as z9 } from "zod";
885
+ import { z as z10 } from "zod";
828
886
  var MemGetInputSchema = {
829
- id: z9.string().min(1).describe("Memory id to fetch")
887
+ id: z10.string().min(1).describe("Memory id to fetch")
830
888
  };
831
889
  async function memGet(input, ctx) {
832
- if (!existsSync9(ctx.paths.memoriesDir)) {
890
+ if (!existsSync10(ctx.paths.memoriesDir)) {
833
891
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
834
892
  }
835
- const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
893
+ const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
836
894
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
837
895
  if (!found) throw new Error(`No memory with id "${input.id}".`);
838
896
  const fm = found.memory.frontmatter;
839
- const u = getUsage3(await loadUsageIndex4(ctx.paths), fm.id);
897
+ const u = getUsage4(await loadUsageIndex5(ctx.paths), fm.id);
840
898
  return {
841
899
  id: fm.id,
842
900
  scope: fm.scope,
@@ -861,32 +919,32 @@ async function memGet(input, ctx) {
861
919
  }
862
920
 
863
921
  // src/tools/mem-delete.ts
864
- import { existsSync as existsSync10 } from "fs";
922
+ import { existsSync as existsSync11 } from "fs";
865
923
  import { unlink } from "fs/promises";
866
924
  import {
867
- loadMemoriesFromDir as loadMemoriesFromDir8,
868
- loadUsageIndex as loadUsageIndex5,
869
- saveUsageIndex as saveUsageIndex2
925
+ loadMemoriesFromDir as loadMemoriesFromDir9,
926
+ loadUsageIndex as loadUsageIndex6,
927
+ saveUsageIndex as saveUsageIndex3
870
928
  } from "@hiveai/core";
871
- import { z as z10 } from "zod";
929
+ import { z as z11 } from "zod";
872
930
  var MemDeleteInputSchema = {
873
- id: z10.string().min(1).describe("Memory id to delete"),
874
- keep_usage: z10.boolean().default(false).describe("Keep the usage.json entry instead of removing it alongside the file")
931
+ id: z11.string().min(1).describe("Memory id to delete"),
932
+ keep_usage: z11.boolean().default(false).describe("Keep the usage.json entry instead of removing it alongside the file")
875
933
  };
876
934
  async function memDelete(input, ctx) {
877
- if (!existsSync10(ctx.paths.memoriesDir)) {
935
+ if (!existsSync11(ctx.paths.memoriesDir)) {
878
936
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
879
937
  }
880
- const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
938
+ const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
881
939
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
882
940
  if (!found) throw new Error(`No memory with id "${input.id}".`);
883
941
  await unlink(found.filePath);
884
942
  let usageRemoved = false;
885
943
  if (!input.keep_usage) {
886
- const idx = await loadUsageIndex5(ctx.paths);
944
+ const idx = await loadUsageIndex6(ctx.paths);
887
945
  if (idx.by_id[input.id]) {
888
946
  delete idx.by_id[input.id];
889
- await saveUsageIndex2(ctx.paths, idx);
947
+ await saveUsageIndex3(ctx.paths, idx);
890
948
  usageRemoved = true;
891
949
  }
892
950
  }
@@ -895,24 +953,24 @@ async function memDelete(input, ctx) {
895
953
 
896
954
  // src/tools/mem-update.ts
897
955
  import { writeFile as writeFile5 } from "fs/promises";
898
- import { existsSync as existsSync11 } from "fs";
899
- import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
900
- import { z as z11 } from "zod";
956
+ import { existsSync as existsSync12 } from "fs";
957
+ import { loadMemoriesFromDir as loadMemoriesFromDir10, serializeMemory as serializeMemory4 } from "@hiveai/core";
958
+ import { z as z12 } from "zod";
901
959
  var MemUpdateInputSchema = {
902
- id: z11.string().min(1).describe("Id of the memory to update"),
903
- body: z11.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
904
- tags: z11.array(z11.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
905
- paths: z11.array(z11.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
906
- symbols: z11.array(z11.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
907
- commit: z11.string().optional().describe("New anchor commit SHA"),
908
- domain: z11.string().optional().describe("New domain label"),
909
- author: z11.string().optional().describe("New author handle or email")
960
+ id: z12.string().min(1).describe("Id of the memory to update"),
961
+ body: z12.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
962
+ tags: z12.array(z12.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
963
+ paths: z12.array(z12.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
964
+ symbols: z12.array(z12.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
965
+ commit: z12.string().optional().describe("New anchor commit SHA"),
966
+ domain: z12.string().optional().describe("New domain label"),
967
+ author: z12.string().optional().describe("New author handle or email")
910
968
  };
911
969
  async function memUpdate(input, ctx) {
912
- if (!existsSync11(ctx.paths.memoriesDir)) {
970
+ if (!existsSync12(ctx.paths.memoriesDir)) {
913
971
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
914
972
  }
915
- const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
973
+ const memories = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
916
974
  const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
917
975
  if (!loaded) throw new Error(`No memory with id "${input.id}".`);
918
976
  const { frontmatter, body } = loaded.memory;
@@ -954,20 +1012,20 @@ async function memUpdate(input, ctx) {
954
1012
  }
955
1013
 
956
1014
  // src/tools/mem-pending.ts
957
- import { existsSync as existsSync12 } from "fs";
1015
+ import { existsSync as existsSync13 } from "fs";
958
1016
  import {
959
- getUsage as getUsage4,
960
- loadMemoriesFromDir as loadMemoriesFromDir10,
961
- loadUsageIndex as loadUsageIndex6
1017
+ getUsage as getUsage5,
1018
+ loadMemoriesFromDir as loadMemoriesFromDir11,
1019
+ loadUsageIndex as loadUsageIndex7
962
1020
  } from "@hiveai/core";
963
- import { z as z12 } from "zod";
1021
+ import { z as z13 } from "zod";
964
1022
  var MemPendingInputSchema = {
965
- scope: z12.enum(["personal", "team", "module"]).optional()
1023
+ scope: z13.enum(["personal", "team", "module"]).optional()
966
1024
  };
967
1025
  async function memPending(input, ctx) {
968
- if (!existsSync12(ctx.paths.memoriesDir)) return { pending: [] };
969
- const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
970
- const usage = await loadUsageIndex6(ctx.paths);
1026
+ if (!existsSync13(ctx.paths.memoriesDir)) return { pending: [] };
1027
+ const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
1028
+ const usage = await loadUsageIndex7(ctx.paths);
971
1029
  const now = Date.now();
972
1030
  const proposed = all.filter(({ memory }) => {
973
1031
  if (memory.frontmatter.status !== "proposed") return false;
@@ -975,12 +1033,12 @@ async function memPending(input, ctx) {
975
1033
  return true;
976
1034
  });
977
1035
  proposed.sort(
978
- (a, b) => getUsage4(usage, b.memory.frontmatter.id).read_count - getUsage4(usage, a.memory.frontmatter.id).read_count
1036
+ (a, b) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
979
1037
  );
980
1038
  return {
981
1039
  pending: proposed.map(({ memory, filePath }) => {
982
1040
  const fm = memory.frontmatter;
983
- const u = getUsage4(usage, fm.id);
1041
+ const u = getUsage5(usage, fm.id);
984
1042
  return {
985
1043
  id: fm.id,
986
1044
  scope: fm.scope,
@@ -998,20 +1056,20 @@ async function memPending(input, ctx) {
998
1056
 
999
1057
  // src/tools/mem-approve.ts
1000
1058
  import { writeFile as writeFile6 } from "fs/promises";
1001
- import { existsSync as existsSync13 } from "fs";
1059
+ import { existsSync as existsSync14 } from "fs";
1002
1060
  import {
1003
- loadMemoriesFromDir as loadMemoriesFromDir11,
1061
+ loadMemoriesFromDir as loadMemoriesFromDir12,
1004
1062
  serializeMemory as serializeMemory5
1005
1063
  } from "@hiveai/core";
1006
- import { z as z13 } from "zod";
1064
+ import { z as z14 } from "zod";
1007
1065
  var MemApproveInputSchema = {
1008
- id: z13.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
1066
+ id: z14.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
1009
1067
  };
1010
1068
  async function memApprove(input, ctx) {
1011
- if (!existsSync13(ctx.paths.memoriesDir)) {
1069
+ if (!existsSync14(ctx.paths.memoriesDir)) {
1012
1070
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
1013
1071
  }
1014
- const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
1072
+ const all = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
1015
1073
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
1016
1074
  if (!found) throw new Error(`No memory with id "${input.id}".`);
1017
1075
  const previous = found.memory.frontmatter.status;
@@ -1030,7 +1088,7 @@ async function memApprove(input, ctx) {
1030
1088
 
1031
1089
  // src/tools/mem-tried.ts
1032
1090
  import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
1033
- import { existsSync as existsSync14 } from "fs";
1091
+ import { existsSync as existsSync15 } from "fs";
1034
1092
  import path5 from "path";
1035
1093
  import {
1036
1094
  buildFrontmatter as buildFrontmatter2,
@@ -1038,19 +1096,19 @@ import {
1038
1096
  serializeMemory as serializeMemory6,
1039
1097
  suggestSensorFromMemory as suggestSensorFromMemory2
1040
1098
  } from "@hiveai/core";
1041
- import { z as z14 } from "zod";
1099
+ import { z as z15 } from "zod";
1042
1100
  var MemTriedInputSchema = {
1043
- what: z14.string().min(1).describe("Brief description of the approach that was tried"),
1044
- why_failed: z14.string().min(1).describe("Why it failed or why it should NOT be used"),
1045
- instead: z14.string().optional().describe("What to use or do instead (recommended alternative)"),
1046
- scope: z14.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
1047
- module: z14.string().optional().describe("Module name (required when scope=module)"),
1048
- tags: z14.array(z14.string()).default([]).describe("Tags for filtering"),
1049
- paths: z14.array(z14.string()).default([]).describe("Anchor file paths this applies to"),
1050
- author: z14.string().optional().describe("Author handle or email")
1101
+ what: z15.string().min(1).describe("Brief description of the approach that was tried"),
1102
+ why_failed: z15.string().min(1).describe("Why it failed or why it should NOT be used"),
1103
+ instead: z15.string().optional().describe("What to use or do instead (recommended alternative)"),
1104
+ scope: z15.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
1105
+ module: z15.string().optional().describe("Module name (required when scope=module)"),
1106
+ tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
1107
+ paths: z15.array(z15.string()).default([]).describe("Anchor file paths this applies to"),
1108
+ author: z15.string().optional().describe("Author handle or email")
1051
1109
  };
1052
1110
  async function memTried(input, ctx) {
1053
- if (!existsSync14(ctx.paths.haiveDir)) {
1111
+ if (!existsSync15(ctx.paths.haiveDir)) {
1054
1112
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1055
1113
  }
1056
1114
  const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
@@ -1076,7 +1134,7 @@ async function memTried(input, ctx) {
1076
1134
  }
1077
1135
  const file = memoryFilePath2(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
1078
1136
  await mkdir3(path5.dirname(file), { recursive: true });
1079
- if (existsSync14(file)) {
1137
+ if (existsSync15(file)) {
1080
1138
  throw new Error(`Memory already exists at ${file}`);
1081
1139
  }
1082
1140
  await writeFile7(file, serializeMemory6({ frontmatter, body }), "utf8");
@@ -1085,7 +1143,7 @@ async function memTried(input, ctx) {
1085
1143
 
1086
1144
  // src/tools/mem-observe.ts
1087
1145
  import { mkdir as mkdir4, writeFile as writeFile8 } from "fs/promises";
1088
- import { existsSync as existsSync15 } from "fs";
1146
+ import { existsSync as existsSync16 } from "fs";
1089
1147
  import path6 from "path";
1090
1148
  import {
1091
1149
  buildFrontmatter as buildFrontmatter3,
@@ -1093,22 +1151,22 @@ import {
1093
1151
  memoryFilePath as memoryFilePath3,
1094
1152
  serializeMemory as serializeMemory7
1095
1153
  } from "@hiveai/core";
1096
- import { z as z15 } from "zod";
1154
+ import { z as z16 } from "zod";
1097
1155
  var MemObserveInputSchema = {
1098
- what: z15.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
1099
- where: z15.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
1100
- impact: z15.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
1101
- fix: z15.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
1102
- scope: z15.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
1103
- module: z15.string().optional().describe("Module name (required when scope=module)"),
1104
- tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
1105
- author: z15.string().optional().describe("Author handle or email"),
1106
- force: z15.boolean().default(false).describe(
1156
+ what: z16.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
1157
+ where: z16.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
1158
+ impact: z16.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
1159
+ fix: z16.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
1160
+ scope: z16.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
1161
+ module: z16.string().optional().describe("Module name (required when scope=module)"),
1162
+ tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
1163
+ author: z16.string().optional().describe("Author handle or email"),
1164
+ force: z16.boolean().default(false).describe(
1107
1165
  "Save even if the observation looks like generic, guessable knowledge. By default, low-specificity observations (things a capable model already knows) are SKIPPED to keep the corpus high-signal \u2014 only unguessable, team-specific discoveries are worth storing."
1108
1166
  )
1109
1167
  };
1110
1168
  async function memObserve(input, ctx) {
1111
- if (!existsSync15(ctx.paths.haiveDir)) {
1169
+ if (!existsSync16(ctx.paths.haiveDir)) {
1112
1170
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1113
1171
  }
1114
1172
  const signalText = [input.what, input.impact, input.fix ?? ""].join(" ");
@@ -1142,7 +1200,7 @@ async function memObserve(input, ctx) {
1142
1200
  const body = lines.join("\n") + "\n";
1143
1201
  const file = memoryFilePath3(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
1144
1202
  await mkdir4(path6.dirname(file), { recursive: true });
1145
- if (existsSync15(file)) {
1203
+ if (existsSync16(file)) {
1146
1204
  throw new Error(`Memory already exists at ${file}`);
1147
1205
  }
1148
1206
  await writeFile8(file, serializeMemory7({ frontmatter, body }), "utf8");
@@ -1151,15 +1209,15 @@ async function memObserve(input, ctx) {
1151
1209
 
1152
1210
  // src/tools/mem-session-end.ts
1153
1211
  import { writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
1154
- import { existsSync as existsSync17 } from "fs";
1212
+ import { existsSync as existsSync18 } from "fs";
1155
1213
  import path8 from "path";
1156
1214
  import {
1157
1215
  buildFrontmatter as buildFrontmatter4,
1158
- loadMemoriesFromDir as loadMemoriesFromDir12,
1216
+ loadMemoriesFromDir as loadMemoriesFromDir13,
1159
1217
  memoryFilePath as memoryFilePath4,
1160
1218
  serializeMemory as serializeMemory8
1161
1219
  } from "@hiveai/core";
1162
- import { z as z16 } from "zod";
1220
+ import { z as z17 } from "zod";
1163
1221
 
1164
1222
  // src/session-tracker.ts
1165
1223
  import {
@@ -1168,7 +1226,7 @@ import {
1168
1226
  loadConfig as loadConfig2
1169
1227
  } from "@hiveai/core";
1170
1228
  import { mkdir as mkdir5, writeFile as writeFile9, rm } from "fs/promises";
1171
- import { existsSync as existsSync16 } from "fs";
1229
+ import { existsSync as existsSync17 } from "fs";
1172
1230
  import path7 from "path";
1173
1231
  import { execSync } from "child_process";
1174
1232
  function pendingDistillPath(ctx) {
@@ -1251,7 +1309,7 @@ var SessionTracker = class {
1251
1309
  (e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
1252
1310
  );
1253
1311
  const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
1254
- if (!ranPostTask && isSubstantialSession && existsSync16(this.ctx.paths.haiveDir)) {
1312
+ if (!ranPostTask && isSubstantialSession && existsSync17(this.ctx.paths.haiveDir)) {
1255
1313
  try {
1256
1314
  const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
1257
1315
  const payload = {
@@ -1285,7 +1343,7 @@ var SessionTracker = class {
1285
1343
  };
1286
1344
  async function clearPendingDistill(ctx) {
1287
1345
  const p = pendingDistillPath(ctx);
1288
- if (existsSync16(p)) {
1346
+ if (existsSync17(p)) {
1289
1347
  try {
1290
1348
  await rm(p);
1291
1349
  } catch {
@@ -1302,15 +1360,15 @@ function summarizeTools(events) {
1302
1360
 
1303
1361
  // src/tools/mem-session-end.ts
1304
1362
  var MemSessionEndInputSchema = {
1305
- goal: z16.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
1306
- accomplished: z16.string().describe("What was actually done \u2014 bullet list recommended"),
1307
- discoveries: z16.string().default("").describe(
1363
+ goal: z17.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
1364
+ accomplished: z17.string().describe("What was actually done \u2014 bullet list recommended"),
1365
+ discoveries: z17.string().default("").describe(
1308
1366
  "Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
1309
1367
  ),
1310
- files_touched: z16.array(z16.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
1311
- next_steps: z16.string().default("").describe("What should happen next (for the next session or a teammate)"),
1312
- scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
1313
- module: z16.string().optional().describe("Module name (required when scope=module)")
1368
+ files_touched: z17.array(z17.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
1369
+ next_steps: z17.string().default("").describe("What should happen next (for the next session or a teammate)"),
1370
+ scope: z17.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
1371
+ module: z17.string().optional().describe("Module name (required when scope=module)")
1314
1372
  };
1315
1373
  function recapTopic(scope, module) {
1316
1374
  return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
@@ -1340,7 +1398,7 @@ ${input.next_steps}`);
1340
1398
  return lines.join("\n");
1341
1399
  }
1342
1400
  async function memSessionEnd(input, ctx) {
1343
- if (!existsSync17(ctx.paths.haiveDir)) {
1401
+ if (!existsSync18(ctx.paths.haiveDir)) {
1344
1402
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1345
1403
  }
1346
1404
  const body = buildBody(input);
@@ -1351,12 +1409,12 @@ async function memSessionEnd(input, ctx) {
1351
1409
  return rel.startsWith("..") ? p : rel;
1352
1410
  });
1353
1411
  const invalidPaths = normalizedFiles.filter(
1354
- (p) => !existsSync17(path8.resolve(ctx.paths.root, p))
1412
+ (p) => !existsSync18(path8.resolve(ctx.paths.root, p))
1355
1413
  );
1356
1414
  if (invalidPaths.length > 0) {
1357
1415
  console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
1358
1416
  }
1359
- const existing = existsSync17(ctx.paths.memoriesDir) ? await loadMemoriesFromDir12(ctx.paths.memoriesDir) : [];
1417
+ const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir13(ctx.paths.memoriesDir) : [];
1360
1418
  const topicMatch = existing.find(
1361
1419
  ({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
1362
1420
  );
@@ -1416,14 +1474,16 @@ async function memSessionEnd(input, ctx) {
1416
1474
 
1417
1475
  // src/tools/get-briefing.ts
1418
1476
  import { readFile as readFile4, writeFile as writeFile11 } from "fs/promises";
1419
- import { existsSync as existsSync19 } from "fs";
1477
+ import { existsSync as existsSync20 } from "fs";
1420
1478
  import {
1421
1479
  allocateBudget,
1480
+ computeImpact as computeImpact2,
1422
1481
  DEFAULT_AUTO_PROMOTE_RULE,
1423
1482
  deriveConfidence as deriveConfidence4,
1424
1483
  estimateTokens,
1484
+ evaluateSkillActivation,
1425
1485
  extractActionsBriefBody,
1426
- getUsage as getUsage5,
1486
+ getUsage as getUsage6,
1427
1487
  inferModulesFromPaths as inferModulesFromPaths2,
1428
1488
  isAutoPromoteEligible,
1429
1489
  isDecaying,
@@ -1432,8 +1492,8 @@ import {
1432
1492
  literalMatchesAnyToken as literalMatchesAnyToken2,
1433
1493
  loadCodeMap,
1434
1494
  loadConfig as loadConfig3,
1435
- loadMemoriesFromDir as loadMemoriesFromDir13,
1436
- loadUsageIndex as loadUsageIndex7,
1495
+ loadMemoriesFromDir as loadMemoriesFromDir14,
1496
+ loadUsageIndex as loadUsageIndex8,
1437
1497
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
1438
1498
  queryCodeMap,
1439
1499
  resolveBriefingBudget,
@@ -1445,11 +1505,11 @@ import {
1445
1505
  truncateToTokens,
1446
1506
  writeBriefingMarker
1447
1507
  } from "@hiveai/core";
1448
- import { z as z17 } from "zod";
1508
+ import { z as z18 } from "zod";
1449
1509
 
1450
1510
  // src/tools/briefing-helpers.ts
1451
1511
  import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
1452
- import { existsSync as existsSync18 } from "fs";
1512
+ import { existsSync as existsSync19 } from "fs";
1453
1513
  import path9 from "path";
1454
1514
  import { isGlobPath, isStackPackSeed, pathsOverlap } from "@hiveai/core";
1455
1515
  function compactSummary(body) {
@@ -1576,7 +1636,7 @@ async function trySemanticHits(ctx, task, limit) {
1576
1636
  }
1577
1637
  async function loadModuleContexts2(ctx, modules) {
1578
1638
  if (modules.length === 0) return [];
1579
- if (!existsSync18(ctx.paths.modulesContextDir)) return [];
1639
+ if (!existsSync19(ctx.paths.modulesContextDir)) return [];
1580
1640
  const available = new Set(
1581
1641
  (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
1582
1642
  );
@@ -1584,7 +1644,7 @@ async function loadModuleContexts2(ctx, modules) {
1584
1644
  for (const m of modules) {
1585
1645
  if (!available.has(m)) continue;
1586
1646
  const file = path9.join(ctx.paths.modulesContextDir, m, "context.md");
1587
- if (existsSync18(file)) {
1647
+ if (existsSync19(file)) {
1588
1648
  out.push({ name: m, content: await readFile3(file, "utf8") });
1589
1649
  }
1590
1650
  }
@@ -1593,35 +1653,35 @@ async function loadModuleContexts2(ctx, modules) {
1593
1653
 
1594
1654
  // src/tools/get-briefing.ts
1595
1655
  var GetBriefingInputSchema = {
1596
- task: z17.string().optional().describe(
1656
+ task: z18.string().optional().describe(
1597
1657
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
1598
1658
  ),
1599
- files: z17.array(z17.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
1600
- max_tokens: z17.number().int().positive().default(8e3).describe(
1659
+ files: z18.array(z18.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
1660
+ max_tokens: z18.number().int().positive().default(8e3).describe(
1601
1661
  "Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
1602
1662
  ),
1603
- max_memories: z17.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
1604
- include_project_context: z17.boolean().default(true),
1605
- include_module_contexts: z17.boolean().default(true),
1606
- semantic: z17.boolean().default(true).describe(
1663
+ max_memories: z18.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
1664
+ include_project_context: z18.boolean().default(true),
1665
+ include_module_contexts: z18.boolean().default(true),
1666
+ semantic: z18.boolean().default(true).describe(
1607
1667
  "Use semantic ranking when a task is provided (requires `haive embeddings index`)."
1608
1668
  ),
1609
- include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
1610
- track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
1611
- format: z17.enum(["full", "compact", "actions"]).default("full").describe(
1669
+ include_stale: z18.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
1670
+ track: z18.boolean().default(true).describe("Increment read_count on returned memories"),
1671
+ format: z18.enum(["full", "compact", "actions"]).default("full").describe(
1612
1672
  "Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
1613
1673
  ),
1614
- symbols: z17.array(z17.string()).default([]).describe(
1674
+ symbols: z18.array(z18.string()).default([]).describe(
1615
1675
  "Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `haive index code` to have been run."
1616
1676
  ),
1617
- min_semantic_score: z17.number().min(0).max(1).default(0).describe(
1677
+ min_semantic_score: z18.number().min(0).max(1).default(0).describe(
1618
1678
  "Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
1619
1679
  ),
1620
- budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
1680
+ budget_preset: z18.enum(["quick", "balanced", "deep"]).optional().describe(
1621
1681
  "Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
1622
1682
  )
1623
1683
  };
1624
- var GetBriefingZod = z17.object(GetBriefingInputSchema);
1684
+ var GetBriefingZod = z18.object(GetBriefingInputSchema);
1625
1685
  async function getBriefing(input, ctx) {
1626
1686
  const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
1627
1687
  max_tokens: input.max_tokens,
@@ -1637,8 +1697,8 @@ async function getBriefing(input, ctx) {
1637
1697
  let usage = { version: 1, updated_at: "", by_id: {} };
1638
1698
  let byId = /* @__PURE__ */ new Map();
1639
1699
  let lastSession;
1640
- if (existsSync19(ctx.paths.memoriesDir)) {
1641
- const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
1700
+ if (existsSync20(ctx.paths.memoriesDir)) {
1701
+ const allLoaded = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
1642
1702
  const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
1643
1703
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
1644
1704
  );
@@ -1660,7 +1720,7 @@ async function getBriefing(input, ctx) {
1660
1720
  if (memory.frontmatter.type === "session_recap") return false;
1661
1721
  return true;
1662
1722
  });
1663
- usage = await loadUsageIndex7(ctx.paths);
1723
+ usage = await loadUsageIndex8(ctx.paths);
1664
1724
  byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
1665
1725
  const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
1666
1726
  if (input.task && input.semantic) {
@@ -1682,7 +1742,8 @@ async function getBriefing(input, ctx) {
1682
1742
  }
1683
1743
  return;
1684
1744
  }
1685
- const u = getUsage5(usage, fm.id);
1745
+ const u = getUsage6(usage, fm.id);
1746
+ const imp = computeImpact2(fm, u);
1686
1747
  seen.set(fm.id, {
1687
1748
  id: fm.id,
1688
1749
  scope: fm.scope,
@@ -1693,6 +1754,8 @@ async function getBriefing(input, ctx) {
1693
1754
  confidence: deriveConfidence4(fm, u),
1694
1755
  ...fm.status === "draft" || fm.status === "proposed" ? { unverified: true } : {},
1695
1756
  read_count: u.read_count,
1757
+ impact_score: imp.score,
1758
+ impact_tier: imp.tier,
1696
1759
  reasons: [reason],
1697
1760
  match_quality: matchQuality ?? "partial",
1698
1761
  ...score !== void 0 ? { semantic_score: score } : {},
@@ -1736,11 +1799,28 @@ async function getBriefing(input, ctx) {
1736
1799
  }
1737
1800
  }
1738
1801
  }
1802
+ const activatedSkills = /* @__PURE__ */ new Set();
1803
+ for (const [id, m] of seen) {
1804
+ if (m.type !== "skill") continue;
1805
+ const loaded = byId.get(id);
1806
+ if (!loaded) continue;
1807
+ const act = evaluateSkillActivation(loaded.memory.frontmatter, {
1808
+ task: input.task,
1809
+ files: input.files
1810
+ });
1811
+ if (act.applicable && !act.activated) {
1812
+ seen.delete(id);
1813
+ continue;
1814
+ }
1815
+ if (act.applicable && act.activated) activatedSkills.add(id);
1816
+ }
1739
1817
  const ranked = [...seen.values()].sort((a, b) => {
1740
1818
  const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
1741
1819
  const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
1742
- const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
1743
- const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
1820
+ const impactScore = (m) => (m.impact_score ?? 0) * 3;
1821
+ const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
1822
+ const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + (a.semantic_score ?? 0);
1823
+ const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + (b.semantic_score ?? 0);
1744
1824
  return sb - sa;
1745
1825
  });
1746
1826
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -1756,7 +1836,7 @@ async function getBriefing(input, ctx) {
1756
1836
  memories.push(...ranked.slice(0, briefingMaxMemories));
1757
1837
  if (input.track && memories.length > 0) {
1758
1838
  await trackReads3(ctx.paths, memories.map((m) => m.id));
1759
- const freshUsage = await loadUsageIndex7(ctx.paths);
1839
+ const freshUsage = await loadUsageIndex8(ctx.paths);
1760
1840
  const cfg = await loadConfig3(ctx.paths);
1761
1841
  const rule = {
1762
1842
  minReads: cfg.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads,
@@ -1765,7 +1845,7 @@ async function getBriefing(input, ctx) {
1765
1845
  for (const m of memories) {
1766
1846
  const loaded = byId.get(m.id);
1767
1847
  if (!loaded) continue;
1768
- const u = getUsage5(freshUsage, m.id);
1848
+ const u = getUsage6(freshUsage, m.id);
1769
1849
  if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
1770
1850
  const newFm = { ...loaded.memory.frontmatter, status: "validated" };
1771
1851
  try {
@@ -1777,12 +1857,12 @@ async function getBriefing(input, ctx) {
1777
1857
  }
1778
1858
  }
1779
1859
  }
1780
- const projectContextRaw = input.include_project_context && existsSync19(ctx.paths.projectContext) ? await readFile4(ctx.paths.projectContext, "utf8") : "";
1860
+ const projectContextRaw = input.include_project_context && existsSync20(ctx.paths.projectContext) ? await readFile4(ctx.paths.projectContext, "utf8") : "";
1781
1861
  const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
1782
1862
  const setupWarnings = [];
1783
1863
  let autoContextGenerated = false;
1784
1864
  let projectContext = isTemplateContext ? "" : projectContextRaw;
1785
- if ((isTemplateContext || !existsSync19(ctx.paths.projectContext)) && input.include_project_context) {
1865
+ if ((isTemplateContext || !existsSync20(ctx.paths.projectContext)) && input.include_project_context) {
1786
1866
  const haiveConfig = await loadConfig3(ctx.paths);
1787
1867
  if (haiveConfig.autoContext) {
1788
1868
  const codeMap = await loadCodeMap(ctx.paths);
@@ -1878,7 +1958,7 @@ ${m.content}`).join("\n\n---\n\n"),
1878
1958
  const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
1879
1959
  const decayWarnings = [];
1880
1960
  for (const m of trimmedMemories) {
1881
- const u = getUsage5(usage, m.id);
1961
+ const u = getUsage6(usage, m.id);
1882
1962
  const loaded = byId.get(m.id);
1883
1963
  const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
1884
1964
  if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
@@ -1943,8 +2023,8 @@ ${m.content}`).join("\n\n---\n\n"),
1943
2023
  actionRequired.push(extractActionItem(m.id, loaded.memory.body));
1944
2024
  }
1945
2025
  }
1946
- if (existsSync19(ctx.paths.memoriesDir)) {
1947
- const allMems = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
2026
+ if (existsSync20(ctx.paths.memoriesDir)) {
2027
+ const allMems = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
1948
2028
  for (const { memory } of allMems) {
1949
2029
  const fm = memory.frontmatter;
1950
2030
  if (!fm.requires_human_approval) continue;
@@ -1954,7 +2034,7 @@ ${m.content}`).join("\n\n---\n\n"),
1954
2034
  }
1955
2035
  }
1956
2036
  const pendingDistillFile = pendingDistillPath(ctx);
1957
- if (existsSync19(pendingDistillFile)) {
2037
+ if (existsSync20(pendingDistillFile)) {
1958
2038
  try {
1959
2039
  const raw = await readFile4(pendingDistillFile, "utf8");
1960
2040
  const pd = JSON.parse(raw);
@@ -1983,7 +2063,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
1983
2063
  }
1984
2064
  }
1985
2065
  const memoriesEmpty = outputMemories.length === 0;
1986
- const hasMemoriesDir = existsSync19(ctx.paths.memoriesDir);
2066
+ const hasMemoriesDir = existsSync20(ctx.paths.memoriesDir);
1987
2067
  const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
1988
2068
  const hasUnguessableSignal = outputMemories.some(
1989
2069
  (m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
@@ -2030,7 +2110,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2030
2110
  "No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
2031
2111
  );
2032
2112
  }
2033
- if (existsSync19(ctx.paths.haiveDir)) {
2113
+ if (existsSync20(ctx.paths.haiveDir)) {
2034
2114
  await writeBriefingMarker(ctx.paths, {
2035
2115
  sessionId: process.env.HAIVE_SESSION_ID,
2036
2116
  ...input.task ? { task: input.task } : {},
@@ -2081,19 +2161,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2081
2161
 
2082
2162
  // src/tools/code-map.ts
2083
2163
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
2084
- import { z as z18 } from "zod";
2164
+ import { z as z19 } from "zod";
2085
2165
  var CodeMapInputSchema = {
2086
- file: z18.string().optional().describe("Filter to files whose path contains this substring"),
2087
- symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
2088
- paths: z18.array(z18.string()).default([]).describe(
2166
+ file: z19.string().optional().describe("Filter to files whose path contains this substring"),
2167
+ symbol: z19.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
2168
+ paths: z19.array(z19.string()).default([]).describe(
2089
2169
  "Filter to files under any of these path prefixes (e.g. ['packages/mcp/src/tools/', 'src/auth/']). OR-joined with `file` substring; useful to get a focused view of one module."
2090
2170
  ),
2091
- max_files: z18.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
2092
- max_tokens: z18.number().int().positive().optional().describe(
2171
+ max_files: z19.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
2172
+ max_tokens: z19.number().int().positive().optional().describe(
2093
2173
  "Approximate token budget for the response. When the matching set exceeds it, files are ranked by export density (exports per LOC) and the highest-signal ones are kept first. Omit to disable budgeting (legacy behavior)."
2094
2174
  )
2095
2175
  };
2096
- var CodeMapInputZod = z18.object(CodeMapInputSchema);
2176
+ var CodeMapInputZod = z19.object(CodeMapInputSchema);
2097
2177
  async function codeMapTool(input, ctx) {
2098
2178
  const map = await loadCodeMap2(ctx.paths);
2099
2179
  if (!map) {
@@ -2162,18 +2242,18 @@ function estimateFileEntryTokens(f) {
2162
2242
  }
2163
2243
 
2164
2244
  // src/tools/mem-diff.ts
2165
- import { existsSync as existsSync20 } from "fs";
2166
- import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
2167
- import { z as z19 } from "zod";
2245
+ import { existsSync as existsSync21 } from "fs";
2246
+ import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
2247
+ import { z as z20 } from "zod";
2168
2248
  var MemDiffInputSchema = {
2169
- id_a: z19.string().min(1).describe("First memory id"),
2170
- id_b: z19.string().min(1).describe("Second memory id")
2249
+ id_a: z20.string().min(1).describe("First memory id"),
2250
+ id_b: z20.string().min(1).describe("Second memory id")
2171
2251
  };
2172
2252
  async function memDiff(input, ctx) {
2173
- if (!existsSync20(ctx.paths.memoriesDir)) {
2253
+ if (!existsSync21(ctx.paths.memoriesDir)) {
2174
2254
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
2175
2255
  }
2176
- const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
2256
+ const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
2177
2257
  const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
2178
2258
  const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
2179
2259
  if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
@@ -2207,19 +2287,19 @@ async function memDiff(input, ctx) {
2207
2287
  }
2208
2288
 
2209
2289
  // src/tools/get-recap.ts
2210
- import { existsSync as existsSync21 } from "fs";
2211
- import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
2212
- import { z as z20 } from "zod";
2290
+ import { existsSync as existsSync22 } from "fs";
2291
+ import { loadMemoriesFromDir as loadMemoriesFromDir16 } from "@hiveai/core";
2292
+ import { z as z21 } from "zod";
2213
2293
  var GetRecapInputSchema = {
2214
- scope: z20.enum(["personal", "team", "any"]).default("any").describe(
2294
+ scope: z21.enum(["personal", "team", "any"]).default("any").describe(
2215
2295
  "Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
2216
2296
  )
2217
2297
  };
2218
2298
  async function getRecap(input, ctx) {
2219
- if (!existsSync21(ctx.paths.memoriesDir)) {
2299
+ if (!existsSync22(ctx.paths.memoriesDir)) {
2220
2300
  return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
2221
2301
  }
2222
- const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
2302
+ const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
2223
2303
  const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
2224
2304
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
2225
2305
  );
@@ -2243,13 +2323,13 @@ async function getRecap(input, ctx) {
2243
2323
  }
2244
2324
 
2245
2325
  // src/tools/mem-relevant-to.ts
2246
- import { z as z21 } from "zod";
2326
+ import { z as z22 } from "zod";
2247
2327
  var MemRelevantToInputSchema = {
2248
- task: z21.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
2249
- files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
2250
- limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
2251
- min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
2252
- format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
2328
+ task: z22.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
2329
+ files: z22.array(z22.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
2330
+ limit: z22.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
2331
+ min_semantic_score: z22.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
2332
+ format: z22.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
2253
2333
  };
2254
2334
  async function memRelevantTo(input, ctx) {
2255
2335
  const briefingInput = {
@@ -2279,13 +2359,13 @@ async function memRelevantTo(input, ctx) {
2279
2359
  }
2280
2360
 
2281
2361
  // src/tools/code-search.ts
2282
- import { z as z22 } from "zod";
2362
+ import { z as z23 } from "zod";
2283
2363
  var CodeSearchInputSchema = {
2284
- query: z22.string().min(1).describe(
2364
+ query: z23.string().min(1).describe(
2285
2365
  "Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
2286
2366
  ),
2287
- k: z22.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
2288
- min_score: z22.number().min(0).max(1).default(0.2).describe(
2367
+ k: z23.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
2368
+ min_score: z23.number().min(0).max(1).default(0.2).describe(
2289
2369
  "Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
2290
2370
  )
2291
2371
  };
@@ -2315,27 +2395,27 @@ async function codeSearch(input, ctx) {
2315
2395
  }
2316
2396
 
2317
2397
  // src/tools/why-this-file.ts
2318
- import { existsSync as existsSync22 } from "fs";
2398
+ import { existsSync as existsSync23 } from "fs";
2319
2399
  import { spawn } from "child_process";
2320
2400
  import path10 from "path";
2321
2401
  import {
2322
2402
  deriveConfidence as deriveConfidence5,
2323
- getUsage as getUsage6,
2403
+ getUsage as getUsage7,
2324
2404
  loadCodeMap as loadCodeMap3,
2325
- loadMemoriesFromDir as loadMemoriesFromDir16,
2326
- loadUsageIndex as loadUsageIndex8,
2405
+ loadMemoriesFromDir as loadMemoriesFromDir17,
2406
+ loadUsageIndex as loadUsageIndex9,
2327
2407
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
2328
2408
  } from "@hiveai/core";
2329
- import { z as z23 } from "zod";
2409
+ import { z as z24 } from "zod";
2330
2410
  var WhyThisFileInputSchema = {
2331
- path: z23.string().min(1).describe(
2411
+ path: z24.string().min(1).describe(
2332
2412
  "Project-relative path to the file you want context on (e.g. 'packages/mcp/src/tools/mem-save.ts')."
2333
2413
  ),
2334
- git_log_limit: z23.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
2335
- memory_limit: z23.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
2414
+ git_log_limit: z24.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
2415
+ memory_limit: z24.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
2336
2416
  };
2337
2417
  async function whyThisFile(input, ctx) {
2338
- const fileExists = existsSync22(path10.join(ctx.paths.root, input.path));
2418
+ const fileExists = existsSync23(path10.join(ctx.paths.root, input.path));
2339
2419
  const [commits, memories, codeMap] = await Promise.all([
2340
2420
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
2341
2421
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -2376,16 +2456,16 @@ async function whyThisFile(input, ctx) {
2376
2456
  };
2377
2457
  }
2378
2458
  async function collectAnchoredMemories(ctx, filePath, limit) {
2379
- if (!existsSync22(ctx.paths.memoriesDir)) return [];
2380
- const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
2381
- const usage = await loadUsageIndex8(ctx.paths);
2459
+ if (!existsSync23(ctx.paths.memoriesDir)) return [];
2460
+ const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2461
+ const usage = await loadUsageIndex9(ctx.paths);
2382
2462
  const out = [];
2383
2463
  for (const { memory } of all) {
2384
2464
  const fm = memory.frontmatter;
2385
2465
  if (fm.status === "rejected" || fm.status === "deprecated") continue;
2386
2466
  if (fm.type === "session_recap") continue;
2387
2467
  if (!memoryMatchesAnchorPaths3(memory, [filePath])) continue;
2388
- const u = getUsage6(usage, fm.id);
2468
+ const u = getUsage7(usage, fm.id);
2389
2469
  out.push({
2390
2470
  id: fm.id,
2391
2471
  type: fm.type,
@@ -2431,33 +2511,33 @@ function runCommand(cmd, args, cwd) {
2431
2511
  }
2432
2512
 
2433
2513
  // src/tools/anti-patterns-check.ts
2434
- import { existsSync as existsSync23 } from "fs";
2514
+ import { existsSync as existsSync24 } from "fs";
2435
2515
  import {
2436
2516
  addedLinesFromDiff,
2437
2517
  deriveConfidence as deriveConfidence6,
2438
- getUsage as getUsage7,
2518
+ getUsage as getUsage8,
2439
2519
  isRetiredMemory as isRetiredMemory2,
2440
- loadMemoriesFromDir as loadMemoriesFromDir17,
2441
- loadUsageIndex as loadUsageIndex9,
2520
+ loadMemoriesFromDir as loadMemoriesFromDir18,
2521
+ loadUsageIndex as loadUsageIndex10,
2442
2522
  literalMatchesAnyToken as literalMatchesAnyToken3,
2443
2523
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths4,
2444
2524
  runSensors,
2445
2525
  sensorTargetsFromDiff,
2446
2526
  tokenizeQuery as tokenizeQuery3
2447
2527
  } from "@hiveai/core";
2448
- import { z as z24 } from "zod";
2528
+ import { z as z25 } from "zod";
2449
2529
  var AntiPatternsCheckInputSchema = {
2450
- diff: z24.string().optional().describe(
2530
+ diff: z25.string().optional().describe(
2451
2531
  "Raw unified diff text (or any code/text snippet) to scan for previously documented anti-patterns. Tokens from the diff are used to match memory bodies and the embeddings index."
2452
2532
  ),
2453
- paths: z24.array(z24.string()).default([]).describe(
2533
+ paths: z25.array(z25.string()).default([]).describe(
2454
2534
  "File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
2455
2535
  ),
2456
- limit: z24.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
2457
- semantic: z24.boolean().default(true).describe(
2536
+ limit: z25.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
2537
+ semantic: z25.boolean().default(true).describe(
2458
2538
  "When true, also use semantic search (requires @hiveai/embeddings + memory index) to find related anti-patterns."
2459
2539
  ),
2460
- min_semantic_score: z24.number().min(0).max(1).default(0.45).describe(
2540
+ min_semantic_score: z25.number().min(0).max(1).default(0.45).describe(
2461
2541
  "Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
2462
2542
  )
2463
2543
  };
@@ -2525,10 +2605,10 @@ async function antiPatternsCheck(input, ctx) {
2525
2605
  notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
2526
2606
  };
2527
2607
  }
2528
- if (!existsSync23(ctx.paths.memoriesDir)) {
2608
+ if (!existsSync24(ctx.paths.memoriesDir)) {
2529
2609
  return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
2530
2610
  }
2531
- const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2611
+ const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
2532
2612
  const minSemanticScore = input.min_semantic_score ?? 0.45;
2533
2613
  const negative = all.filter(({ memory }) => {
2534
2614
  const t = memory.frontmatter.type;
@@ -2539,7 +2619,7 @@ async function antiPatternsCheck(input, ctx) {
2539
2619
  if (negative.length === 0) {
2540
2620
  return { scanned: 0, warnings: [], notice: "No attempt/gotcha memories found yet." };
2541
2621
  }
2542
- const usage = await loadUsageIndex9(ctx.paths);
2622
+ const usage = await loadUsageIndex10(ctx.paths);
2543
2623
  const seen = /* @__PURE__ */ new Map();
2544
2624
  const upsert = (fm, body, reason, score) => {
2545
2625
  const existing = seen.get(fm.id);
@@ -2550,7 +2630,7 @@ async function antiPatternsCheck(input, ctx) {
2550
2630
  }
2551
2631
  return;
2552
2632
  }
2553
- const u = getUsage7(usage, fm.id);
2633
+ const u = getUsage8(usage, fm.id);
2554
2634
  seen.set(fm.id, {
2555
2635
  id: fm.id,
2556
2636
  type: fm.type,
@@ -2629,19 +2709,19 @@ async function antiPatternsCheck(input, ctx) {
2629
2709
  }
2630
2710
 
2631
2711
  // src/tools/mem-distill.ts
2632
- import { existsSync as existsSync24 } from "fs";
2712
+ import { existsSync as existsSync25 } from "fs";
2633
2713
  import {
2634
- loadMemoriesFromDir as loadMemoriesFromDir18,
2714
+ loadMemoriesFromDir as loadMemoriesFromDir19,
2635
2715
  tokenizeQuery as tokenizeQuery4
2636
2716
  } from "@hiveai/core";
2637
- import { z as z25 } from "zod";
2717
+ import { z as z26 } from "zod";
2638
2718
  var MemDistillInputSchema = {
2639
- since_days: z25.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
2640
- min_cluster: z25.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
2641
- type_filter: z25.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
2719
+ since_days: z26.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
2720
+ min_cluster: z26.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
2721
+ type_filter: z26.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
2642
2722
  "Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
2643
2723
  ),
2644
- scope: z25.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
2724
+ scope: z26.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
2645
2725
  };
2646
2726
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
2647
2727
  var STOP_WORDS = /* @__PURE__ */ new Set([
@@ -2681,11 +2761,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
2681
2761
  "error"
2682
2762
  ]);
2683
2763
  async function memDistill(input, ctx) {
2684
- if (!existsSync24(ctx.paths.memoriesDir)) {
2764
+ if (!existsSync25(ctx.paths.memoriesDir)) {
2685
2765
  return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
2686
2766
  }
2687
2767
  const cutoff = Date.now() - input.since_days * MS_PER_DAY;
2688
- const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
2768
+ const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
2689
2769
  const candidates = all.filter(({ memory }) => {
2690
2770
  const fm = memory.frontmatter;
2691
2771
  if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
@@ -2789,22 +2869,22 @@ function firstHeading(body) {
2789
2869
  }
2790
2870
 
2791
2871
  // src/tools/why-this-decision.ts
2792
- import { existsSync as existsSync25 } from "fs";
2872
+ import { existsSync as existsSync26 } from "fs";
2793
2873
  import { spawn as spawn2 } from "child_process";
2794
2874
  import {
2795
2875
  deriveConfidence as deriveConfidence7,
2796
- getUsage as getUsage8,
2797
- loadMemoriesFromDir as loadMemoriesFromDir19,
2798
- loadUsageIndex as loadUsageIndex10,
2876
+ getUsage as getUsage9,
2877
+ loadMemoriesFromDir as loadMemoriesFromDir20,
2878
+ loadUsageIndex as loadUsageIndex11,
2799
2879
  pathsOverlap as singlePathsOverlap
2800
2880
  } from "@hiveai/core";
2801
- import { z as z26 } from "zod";
2881
+ import { z as z27 } from "zod";
2802
2882
  var WhyThisDecisionInputSchema = {
2803
- id: z26.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
2804
- git_log_limit: z26.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
2883
+ id: z27.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
2884
+ git_log_limit: z27.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
2805
2885
  };
2806
2886
  async function whyThisDecision(input, ctx) {
2807
- if (!existsSync25(ctx.paths.memoriesDir)) {
2887
+ if (!existsSync26(ctx.paths.memoriesDir)) {
2808
2888
  return {
2809
2889
  found: false,
2810
2890
  related: [],
@@ -2813,8 +2893,8 @@ async function whyThisDecision(input, ctx) {
2813
2893
  notice: "No .ai/memories directory."
2814
2894
  };
2815
2895
  }
2816
- const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
2817
- const usage = await loadUsageIndex10(ctx.paths);
2896
+ const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
2897
+ const usage = await loadUsageIndex11(ctx.paths);
2818
2898
  const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
2819
2899
  if (!target) {
2820
2900
  return {
@@ -2826,7 +2906,7 @@ async function whyThisDecision(input, ctx) {
2826
2906
  };
2827
2907
  }
2828
2908
  const fm = target.memory.frontmatter;
2829
- const targetUsage = getUsage8(usage, fm.id);
2909
+ const targetUsage = getUsage9(usage, fm.id);
2830
2910
  const decision = {
2831
2911
  id: fm.id,
2832
2912
  type: fm.type,
@@ -2843,7 +2923,7 @@ async function whyThisDecision(input, ctx) {
2843
2923
  const isExplicit = relatedSet.has(memory.frontmatter.id);
2844
2924
  const isBackLink = (memory.frontmatter.related_ids ?? []).includes(fm.id);
2845
2925
  if (!isExplicit && !isBackLink) continue;
2846
- const u = getUsage8(usage, memory.frontmatter.id);
2926
+ const u = getUsage9(usage, memory.frontmatter.id);
2847
2927
  related.push({
2848
2928
  id: memory.frontmatter.id,
2849
2929
  type: memory.frontmatter.type,
@@ -2863,7 +2943,7 @@ async function whyThisDecision(input, ctx) {
2863
2943
  (p) => targetPaths.some((tp) => singlePathsOverlap(p, tp))
2864
2944
  );
2865
2945
  if (overlappingPaths.length === 0) continue;
2866
- const u = getUsage8(usage, memory.frontmatter.id);
2946
+ const u = getUsage9(usage, memory.frontmatter.id);
2867
2947
  path_neighbors.push({
2868
2948
  id: memory.frontmatter.id,
2869
2949
  type: memory.frontmatter.type,
@@ -2936,33 +3016,33 @@ function runCommand2(cmd, args, cwd) {
2936
3016
  }
2937
3017
 
2938
3018
  // src/tools/mem-conflicts.ts
2939
- import { existsSync as existsSync26 } from "fs";
3019
+ import { existsSync as existsSync27 } from "fs";
2940
3020
  import {
2941
3021
  deriveConfidence as deriveConfidence8,
2942
- getUsage as getUsage9,
2943
- loadMemoriesFromDir as loadMemoriesFromDir20,
2944
- loadUsageIndex as loadUsageIndex11,
3022
+ getUsage as getUsage10,
3023
+ loadMemoriesFromDir as loadMemoriesFromDir21,
3024
+ loadUsageIndex as loadUsageIndex12,
2945
3025
  pathsOverlap as pathsOverlap2,
2946
3026
  tokenizeQuery as tokenizeQuery5
2947
3027
  } from "@hiveai/core";
2948
- import { z as z27 } from "zod";
3028
+ import { z as z28 } from "zod";
2949
3029
  var MemConflictsInputSchema = {
2950
- id: z27.string().min(1).describe("Memory id to check for conflicts."),
2951
- min_score: z27.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
2952
- semantic: z27.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
3030
+ id: z28.string().min(1).describe("Memory id to check for conflicts."),
3031
+ min_score: z28.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
3032
+ semantic: z28.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
2953
3033
  };
2954
3034
  var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
2955
3035
  var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
2956
3036
  async function memConflicts(input, ctx) {
2957
- if (!existsSync26(ctx.paths.memoriesDir)) {
3037
+ if (!existsSync27(ctx.paths.memoriesDir)) {
2958
3038
  return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
2959
3039
  }
2960
- const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
3040
+ const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
2961
3041
  const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
2962
3042
  if (!target) {
2963
3043
  return { found: false, scanned: 0, conflicts: [], notice: `Memory '${input.id}' not found.` };
2964
3044
  }
2965
- const usage = await loadUsageIndex11(ctx.paths);
3045
+ const usage = await loadUsageIndex12(ctx.paths);
2966
3046
  const others = all.filter(
2967
3047
  ({ memory }) => memory.frontmatter.id !== input.id && memory.frontmatter.type !== "session_recap"
2968
3048
  );
@@ -3003,7 +3083,7 @@ async function memConflicts(input, ctx) {
3003
3083
  }
3004
3084
  }
3005
3085
  if (reasons.length === 0) continue;
3006
- const u = getUsage9(usage, fm.id);
3086
+ const u = getUsage10(usage, fm.id);
3007
3087
  conflicts.push({
3008
3088
  id: fm.id,
3009
3089
  type: fm.type,
@@ -3069,17 +3149,17 @@ async function trySemanticSimilarities(ctx, target, others) {
3069
3149
  }
3070
3150
 
3071
3151
  // src/tools/precommit-check.ts
3072
- import { z as z28 } from "zod";
3152
+ import { z as z29 } from "zod";
3073
3153
  var PreCommitCheckInputSchema = {
3074
- diff: z28.string().optional().describe(
3154
+ diff: z29.string().optional().describe(
3075
3155
  "Raw unified diff text to scan. If omitted, only `paths` is used. When called from a pre-commit hook, pipe the output of `git diff --cached`."
3076
3156
  ),
3077
- paths: z28.array(z28.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
3078
- block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
3157
+ paths: z29.array(z29.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
3158
+ block_on: z29.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
3079
3159
  "When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
3080
3160
  ),
3081
- semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
3082
- anchored_blocks: z28.boolean().default(false).describe(
3161
+ semantic: z29.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
3162
+ anchored_blocks: z29.boolean().default(false).describe(
3083
3163
  "When true, ALSO block a high-confidence anti-pattern (attempt/gotcha) that is anchored to a touched file AND corroborated by the diff (literal token overlap, or semantic >= 0.45) \u2014 not just very strong semantic matches. Powers the 'anchored' enforcement gate. Config/docs-only commits are still downgraded. Default false preserves the soft, semantic-only blocking behavior."
3084
3164
  )
3085
3165
  };
@@ -3147,7 +3227,9 @@ async function preCommitCheck(input, ctx) {
3147
3227
  };
3148
3228
  }
3149
3229
  function classifyWarning(warning, paths, anchoredBlocks = false) {
3150
- const affectedFiles = paths.filter((p) => !p.startsWith(".ai/.usage/"));
3230
+ const affectedFiles = paths.filter(
3231
+ (p) => !p.startsWith(".ai/.usage/") && !p.startsWith(".ai/.cache/") && !p.startsWith(".ai/.runtime/")
3232
+ );
3151
3233
  const repairCommand = repairCommandForWarning(warning, affectedFiles);
3152
3234
  const fileDowngrade = fileTypeDowngradeReason(warning, affectedFiles);
3153
3235
  if (fileDowngrade) {
@@ -3178,6 +3260,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
3178
3260
  };
3179
3261
  }
3180
3262
  if (isBlockingWarning(warning)) {
3263
+ if (warning.scope === "personal") {
3264
+ return {
3265
+ ...warning,
3266
+ level: "review",
3267
+ rationale: "personal anti-pattern memories are review guidance unless a deterministic block-severity sensor fires",
3268
+ affected_files: affectedFiles,
3269
+ repair_command: repairCommand
3270
+ };
3271
+ }
3181
3272
  if (warning.has_sensor && !warning.reasons.includes("sensor")) {
3182
3273
  return {
3183
3274
  ...warning,
@@ -3198,7 +3289,7 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
3198
3289
  const hasSemantic = warning.reasons.includes("semantic");
3199
3290
  const semanticScore = warning.semantic_score ?? 0;
3200
3291
  const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
3201
- if (anchoredBlocks && highConfidence && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
3292
+ if (anchoredBlocks && highConfidence && warning.scope !== "personal" && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
3202
3293
  if (warning.has_sensor && !warning.reasons.includes("sensor")) {
3203
3294
  return {
3204
3295
  ...warning,
@@ -3332,13 +3423,25 @@ function isJsonConfigFile(base) {
3332
3423
  return false;
3333
3424
  }
3334
3425
  function repairCommandForWarning(warning, paths) {
3335
- const firstPath = paths[0];
3336
- return firstPath ? `haive briefing --files "${firstPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
3426
+ const targetPath = repairTargetPathForWarning(warning, paths);
3427
+ return targetPath ? `haive briefing --files "${targetPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
3428
+ }
3429
+ function repairTargetPathForWarning(warning, paths) {
3430
+ const usablePaths = paths.filter(
3431
+ (p) => !p.startsWith(".ai/.usage/") && !p.startsWith(".ai/.cache/") && !p.startsWith(".ai/.runtime/")
3432
+ );
3433
+ const anchors = warning.anchor_paths ?? [];
3434
+ for (const file of usablePaths) {
3435
+ if (anchors.some((anchor) => anchor === file || file.startsWith(`${anchor}/`) || anchor.startsWith(`${file}/`))) {
3436
+ return file;
3437
+ }
3438
+ }
3439
+ return usablePaths[0];
3337
3440
  }
3338
3441
 
3339
3442
  // src/tools/pattern-detect.ts
3340
3443
  import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
3341
- import { existsSync as existsSync27 } from "fs";
3444
+ import { existsSync as existsSync28 } from "fs";
3342
3445
  import path11 from "path";
3343
3446
  import { execSync as execSync2 } from "child_process";
3344
3447
  import {
@@ -3347,7 +3450,7 @@ import {
3347
3450
  readUsageEvents,
3348
3451
  serializeMemory as serializeMemory10
3349
3452
  } from "@hiveai/core";
3350
- import { z as z29 } from "zod";
3453
+ import { z as z30 } from "zod";
3351
3454
  var CONFIG_PATTERNS = [
3352
3455
  ".eslintrc",
3353
3456
  "eslint.config",
@@ -3370,12 +3473,12 @@ var CONFIG_PATTERNS = [
3370
3473
  var MAX_DIFF_BYTES = 4096;
3371
3474
  var HOT_FILE_MIN = 3;
3372
3475
  var PatternDetectInputSchema = {
3373
- since_days: z29.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
3374
- dry_run: z29.boolean().default(false).describe("When true, report matches without writing any memory files."),
3375
- scope: z29.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
3476
+ since_days: z30.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
3477
+ dry_run: z30.boolean().default(false).describe("When true, report matches without writing any memory files."),
3478
+ scope: z30.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
3376
3479
  };
3377
3480
  async function patternDetect(input, ctx) {
3378
- if (!existsSync27(ctx.paths.haiveDir)) {
3481
+ if (!existsSync28(ctx.paths.haiveDir)) {
3379
3482
  return {
3380
3483
  scanned_events: 0,
3381
3484
  matches: [],
@@ -3504,7 +3607,7 @@ async function patternDetect(input, ctx) {
3504
3607
  fm.id,
3505
3608
  void 0
3506
3609
  );
3507
- if (existsSync27(file)) continue;
3610
+ if (existsSync28(file)) continue;
3508
3611
  await mkdir7(path11.dirname(file), { recursive: true });
3509
3612
  await writeFile12(
3510
3613
  file,
@@ -3550,25 +3653,25 @@ function gitFileDiff(root, file, sinceDays) {
3550
3653
  }
3551
3654
 
3552
3655
  // src/tools/mem-conflict-candidates.ts
3553
- import { existsSync as existsSync28 } from "fs";
3656
+ import { existsSync as existsSync29 } from "fs";
3554
3657
  import {
3555
3658
  findLexicalConflictPairs,
3556
3659
  findTopicStatusConflictPairs,
3557
- loadMemoriesFromDir as loadMemoriesFromDir21
3660
+ loadMemoriesFromDir as loadMemoriesFromDir22
3558
3661
  } from "@hiveai/core";
3559
- import { z as z30 } from "zod";
3662
+ import { z as z31 } from "zod";
3560
3663
  var MemConflictCandidatesInputSchema = {
3561
- since_days: z30.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3562
- types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
3563
- min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
3564
- max_pairs: z30.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
3565
- max_scan: z30.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
3566
- max_topic_pairs: z30.number().int().positive().max(100).default(20).describe(
3664
+ since_days: z31.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3665
+ types: z31.array(z31.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
3666
+ min_jaccard: z31.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
3667
+ max_pairs: z31.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
3668
+ max_scan: z31.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
3669
+ max_topic_pairs: z31.number().int().positive().max(100).default(20).describe(
3567
3670
  "Cap for extra signal: memories sharing the same topic with validated vs rejected status."
3568
3671
  )
3569
3672
  };
3570
3673
  async function memConflictCandidates(input, ctx) {
3571
- if (!existsSync28(ctx.paths.memoriesDir)) {
3674
+ if (!existsSync29(ctx.paths.memoriesDir)) {
3572
3675
  return {
3573
3676
  pairs: [],
3574
3677
  topic_status_pairs: [],
@@ -3577,7 +3680,7 @@ async function memConflictCandidates(input, ctx) {
3577
3680
  notice: "No .ai/memories directory."
3578
3681
  };
3579
3682
  }
3580
- const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
3683
+ const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
3581
3684
  const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
3582
3685
  sinceDays: input.since_days,
3583
3686
  types: input.types,
@@ -3592,9 +3695,9 @@ async function memConflictCandidates(input, ctx) {
3592
3695
 
3593
3696
  // src/tools/mem-resolve-project.ts
3594
3697
  import { resolveProjectInfo } from "@hiveai/core";
3595
- import { z as z31 } from "zod";
3698
+ import { z as z32 } from "zod";
3596
3699
  var MemResolveProjectInputSchema = {
3597
- cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
3700
+ cwd: z32.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
3598
3701
  };
3599
3702
  async function memResolveProject(input, _ctx) {
3600
3703
  void _ctx;
@@ -3608,10 +3711,10 @@ async function memResolveProject(input, _ctx) {
3608
3711
 
3609
3712
  // src/tools/mem-suggest-topic.ts
3610
3713
  import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3611
- import { z as z32 } from "zod";
3714
+ import { z as z33 } from "zod";
3612
3715
  var MemSuggestTopicInputSchema = {
3613
3716
  type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
3614
- title: z32.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
3717
+ title: z33.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
3615
3718
  };
3616
3719
  async function memSuggestTopic(input, _ctx) {
3617
3720
  void _ctx;
@@ -3620,19 +3723,19 @@ async function memSuggestTopic(input, _ctx) {
3620
3723
  }
3621
3724
 
3622
3725
  // src/tools/mem-timeline.ts
3623
- import { existsSync as existsSync29 } from "fs";
3624
- import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hiveai/core";
3625
- import { z as z33 } from "zod";
3726
+ import { existsSync as existsSync30 } from "fs";
3727
+ import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hiveai/core";
3728
+ import { z as z34 } from "zod";
3626
3729
  var MemTimelineInputSchema = {
3627
- memory_id: z33.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
3628
- topic: z33.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
3629
- limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
3730
+ memory_id: z34.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
3731
+ topic: z34.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
3732
+ limit: z34.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
3630
3733
  };
3631
3734
  async function memTimeline(input, ctx) {
3632
- if (!existsSync29(ctx.paths.memoriesDir)) {
3735
+ if (!existsSync30(ctx.paths.memoriesDir)) {
3633
3736
  return { entries: [], total: 0, notice: "No .ai/memories directory." };
3634
3737
  }
3635
- const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
3738
+ const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
3636
3739
  const { entries, notice } = collectTimelineEntries(all, {
3637
3740
  memoryId: input.memory_id,
3638
3741
  topic: input.topic,
@@ -3643,11 +3746,11 @@ async function memTimeline(input, ctx) {
3643
3746
 
3644
3747
  // src/tools/runtime-journal-append.ts
3645
3748
  import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
3646
- import { z as z34 } from "zod";
3749
+ import { z as z35 } from "zod";
3647
3750
  var RuntimeJournalAppendInputSchema = {
3648
- message: z34.string().min(1).describe("Short line to append to the runtime session journal"),
3649
- kind: z34.enum(["note", "session_end", "mcp"]).default("note"),
3650
- tool: z34.string().optional().describe("When kind=mcp, which tool name (optional)")
3751
+ message: z35.string().min(1).describe("Short line to append to the runtime session journal"),
3752
+ kind: z35.enum(["note", "session_end", "mcp"]).default("note"),
3753
+ tool: z35.string().optional().describe("When kind=mcp, which tool name (optional)")
3651
3754
  };
3652
3755
  async function runtimeJournalAppend(input, ctx) {
3653
3756
  await appendRuntimeJournalEntry2(ctx.paths, {
@@ -3663,9 +3766,9 @@ async function runtimeJournalAppend(input, ctx) {
3663
3766
 
3664
3767
  // src/tools/runtime-journal-tail.ts
3665
3768
  import { readRuntimeJournalTail } from "@hiveai/core";
3666
- import { z as z35 } from "zod";
3769
+ import { z as z36 } from "zod";
3667
3770
  var RuntimeJournalTailInputSchema = {
3668
- limit: z35.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
3771
+ limit: z36.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
3669
3772
  };
3670
3773
  async function runtimeJournalTail(input, ctx) {
3671
3774
  const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
@@ -3676,12 +3779,12 @@ async function runtimeJournalTail(input, ctx) {
3676
3779
  }
3677
3780
 
3678
3781
  // src/prompts/bootstrap-project.ts
3679
- import { z as z36 } from "zod";
3782
+ import { z as z37 } from "zod";
3680
3783
  var BootstrapProjectArgsSchema = {
3681
- module: z36.string().optional().describe(
3784
+ module: z37.string().optional().describe(
3682
3785
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
3683
3786
  ),
3684
- focus: z36.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3787
+ focus: z37.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3685
3788
  };
3686
3789
  var ROOT_TEMPLATE = `# Project context
3687
3790
 
@@ -3763,10 +3866,10 @@ ${template}\`\`\`
3763
3866
  }
3764
3867
 
3765
3868
  // src/prompts/post-task.ts
3766
- import { z as z37 } from "zod";
3869
+ import { z as z38 } from "zod";
3767
3870
  var PostTaskArgsSchema = {
3768
- task_summary: z37.string().optional().describe("One sentence describing what you just did"),
3769
- files_touched: z37.array(z37.string()).optional().describe("Files you created or modified during the task")
3871
+ task_summary: z38.string().optional().describe("One sentence describing what you just did"),
3872
+ files_touched: z38.array(z38.string()).optional().describe("Files you created or modified during the task")
3770
3873
  };
3771
3874
  function postTaskPrompt(args, ctx) {
3772
3875
  const taskLine = args.task_summary ? `
@@ -3836,7 +3939,19 @@ This creates/updates a single rolling recap that **get_briefing automatically su
3836
3939
 
3837
3940
  Calling \`mem_session_end\` also **clears the pending-distill marker** (if any), confirming that this session's learnings have been properly captured rather than left as an auto-recap skeleton.
3838
3941
 
3839
- When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved."
3942
+ ### 7. Verify the git/release exit protocol \u2014 always
3943
+ Run **\`haive enforce finish\`** before your final response.
3944
+
3945
+ This executable gate checks the multi-agent git-sync decision:
3946
+ - no completed work is left as an uncommitted local diff
3947
+ - shippable package changes have a lockstep version bump
3948
+ - the release tag \`vX.Y.Z\` exists when a version was bumped
3949
+ - commits and tags have been pushed
3950
+ - agents never run \`npm publish\` (publication remains human-owned)
3951
+
3952
+ If it blocks, fix the reported Git/version/tag/push issue before telling the developer the task is done.
3953
+
3954
+ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved. hAIve finish gate passed."
3840
3955
  `;
3841
3956
  return {
3842
3957
  description: "Post-task reflection: capture what you learned before closing the session",
@@ -3850,12 +3965,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
3850
3965
  }
3851
3966
 
3852
3967
  // src/prompts/import-docs.ts
3853
- import { z as z38 } from "zod";
3968
+ import { z as z39 } from "zod";
3854
3969
  var ImportDocsArgsSchema = {
3855
- content: z38.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
3856
- source: z38.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
3857
- scope: z38.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
3858
- dry_run: z38.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
3970
+ content: z39.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
3971
+ source: z39.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
3972
+ scope: z39.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
3973
+ dry_run: z39.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
3859
3974
  };
3860
3975
  function importDocsPrompt(args, ctx) {
3861
3976
  const sourceLine = args.source ? `
@@ -3921,7 +4036,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
3921
4036
  // src/server.ts
3922
4037
  import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
3923
4038
  var SERVER_NAME = "haive";
3924
- var SERVER_VERSION = "0.10.8";
4039
+ var SERVER_VERSION = "0.11.0";
3925
4040
  function jsonResult(data) {
3926
4041
  return {
3927
4042
  content: [
@@ -3963,7 +4078,8 @@ var MAINTENANCE_PROFILE_TOOLS = [
3963
4078
  "anti_patterns_check",
3964
4079
  "mem_distill",
3965
4080
  "mem_timeline",
3966
- "mem_conflict_candidates"
4081
+ "mem_conflict_candidates",
4082
+ "mem_feedback"
3967
4083
  ];
3968
4084
  var EXPERIMENTAL_PROFILE_TOOLS = [
3969
4085
  ...MAINTENANCE_PROFILE_TOOLS,
@@ -3995,6 +4111,7 @@ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
3995
4111
  "mem_approve",
3996
4112
  "mem_reject",
3997
4113
  "mem_delete",
4114
+ "mem_feedback",
3998
4115
  "runtime_journal_append",
3999
4116
  "pattern_detect"
4000
4117
  ]);
@@ -4490,6 +4607,28 @@ function createHaiveServer(options = {}) {
4490
4607
  MemRejectInputSchema,
4491
4608
  async (input) => jsonResult(await memReject(input, context))
4492
4609
  );
4610
+ registerTool(
4611
+ "mem_feedback",
4612
+ [
4613
+ "Record whether a memory actually HELPED \u2014 the closed-loop utility signal.",
4614
+ "",
4615
+ "USE right after a memory changed (or failed to change) what you did:",
4616
+ " - outcome='applied' \u2192 the memory steered your work (strong positive signal)",
4617
+ " - outcome='rejected' \u2192 it was wrong/outdated/unhelpful (negative signal)",
4618
+ "",
4619
+ "A read only means a memory was surfaced; 'applied' means it demonstrably helped.",
4620
+ "This powers `haive memory impact` (impact tiers + prune candidates) and future ranking.",
4621
+ "",
4622
+ "PARAMETERS:",
4623
+ " id \u2014 full memory id the feedback is about",
4624
+ " outcome \u2014 'applied' | 'rejected'",
4625
+ " reason \u2014 why it was rejected (optional, stored on the usage record)",
4626
+ "",
4627
+ "RETURNS: { ok, id, outcome, usage:{read_count,applied_count,rejected_count}, impact:{score,tier,signals} }"
4628
+ ].join("\n"),
4629
+ MemFeedbackInputSchema,
4630
+ async (input) => jsonResult(await memFeedback(input, context))
4631
+ );
4493
4632
  registerTool(
4494
4633
  "mem_pending",
4495
4634
  [