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