@hiveai/mcp 0.2.3 → 0.2.6

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
@@ -124,12 +124,13 @@ import { existsSync as existsSync4 } from "fs";
124
124
  import path3 from "path";
125
125
  import {
126
126
  buildFrontmatter,
127
+ loadMemoriesFromDir as loadMemoriesFromDir2,
127
128
  memoryFilePath,
128
129
  serializeMemory
129
130
  } from "@hiveai/core";
130
131
  import { z as z4 } from "zod";
131
132
  var MemSaveInputSchema = {
132
- type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary"]).describe("Kind of memory being saved"),
133
+ type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).describe("Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated)."),
133
134
  slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
134
135
  body: z4.string().describe("Markdown body of the memory"),
135
136
  scope: z4.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope: personal | team | module"),
@@ -169,11 +170,24 @@ async function memSave(input, ctx) {
169
170
  if (existsSync4(file)) {
170
171
  throw new Error(`Memory already exists at ${file}`);
171
172
  }
173
+ let warning;
174
+ if (existsSync4(ctx.paths.memoriesDir)) {
175
+ const existing = await loadMemoriesFromDir2(ctx.paths.memoriesDir);
176
+ const slugTokens = input.slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
177
+ const similar = existing.filter(({ memory }) => {
178
+ const id = memory.frontmatter.id.toLowerCase();
179
+ return slugTokens.length >= 2 && slugTokens.filter((t) => id.includes(t)).length >= Math.ceil(slugTokens.length * 0.6);
180
+ });
181
+ if (similar.length > 0) {
182
+ warning = `Possible duplicate detected. Similar memories: ${similar.map((m) => m.memory.frontmatter.id).join(", ")}. Consider updating one of these instead.`;
183
+ }
184
+ }
172
185
  await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
173
186
  return {
174
187
  id: frontmatter.id,
175
188
  scope: frontmatter.scope,
176
- file_path: file
189
+ file_path: file,
190
+ ...warning ? { warning } : {}
177
191
  };
178
192
  }
179
193
 
@@ -184,7 +198,8 @@ import {
184
198
  extractSnippet,
185
199
  getUsage,
186
200
  literalMatchesAllTokens,
187
- loadMemoriesFromDir as loadMemoriesFromDir2,
201
+ literalMatchesAnyToken,
202
+ loadMemoriesFromDir as loadMemoriesFromDir3,
188
203
  loadUsageIndex,
189
204
  pickSnippetNeedle,
190
205
  tokenizeQuery,
@@ -194,7 +209,7 @@ import { z as z5 } from "zod";
194
209
  var MemSearchInputSchema = {
195
210
  query: z5.string().describe("Substring matched against id, tags, and body"),
196
211
  scope: z5.enum(["personal", "team", "module"]).optional().describe("Restrict results to a single scope"),
197
- type: z5.enum(["convention", "decision", "gotcha", "architecture", "glossary"]).optional().describe("Restrict results to a memory type"),
212
+ type: z5.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).optional().describe("Restrict results to a memory type"),
198
213
  module: z5.string().optional().describe("Restrict results to a module"),
199
214
  status: z5.enum(["draft", "proposed", "validated", "deprecated", "stale", "rejected"]).optional().describe("Filter by a single status. Omit to return all statuses."),
200
215
  exclude_rejected: z5.boolean().default(false).describe("When true, exclude memories with status=rejected from results."),
@@ -209,7 +224,7 @@ async function memSearch(input, ctx) {
209
224
  if (!existsSync5(ctx.paths.memoriesDir)) {
210
225
  return { matches: [], total: 0, mode: input.semantic ? "literal_fallback" : "literal" };
211
226
  }
212
- const all = await loadMemoriesFromDir2(ctx.paths.memoriesDir);
227
+ const all = await loadMemoriesFromDir3(ctx.paths.memoriesDir);
213
228
  const filtered = all.filter(({ memory }) => passesFilters(memory.frontmatter, input));
214
229
  const usage = await loadUsageIndex(ctx.paths);
215
230
  let result;
@@ -245,13 +260,23 @@ function passesFilters(fm, input) {
245
260
  }
246
261
  function buildLiteralResult(input, filtered, usage) {
247
262
  const tokens = tokenizeQuery(input.query);
248
- const matched = filtered.filter(({ memory }) => literalMatchesAllTokens(memory, tokens));
249
263
  const snippetNeedle = pickSnippetNeedle(input.query);
250
- const top = matched.slice(0, input.limit);
264
+ let andMatched = filtered.filter(({ memory }) => literalMatchesAllTokens(memory, tokens));
265
+ if (andMatched.length > 0) {
266
+ const top2 = andMatched.slice(0, input.limit);
267
+ return {
268
+ matches: top2.map((loaded) => toHit(loaded, snippetNeedle, usage)),
269
+ total: andMatched.length,
270
+ mode: "literal"
271
+ };
272
+ }
273
+ const orMatched = filtered.filter(({ memory }) => literalMatchesAnyToken(memory, tokens));
274
+ const top = orMatched.slice(0, input.limit);
251
275
  return {
252
276
  matches: top.map((loaded) => toHit(loaded, snippetNeedle, usage)),
253
- total: matched.length,
254
- mode: "literal"
277
+ total: orMatched.length,
278
+ mode: "literal",
279
+ notice: `No exact match for all tokens. Showing partial matches (OR fallback) \u2014 ${orMatched.length} result${orMatched.length === 1 ? "" : "s"}.`
255
280
  };
256
281
  }
257
282
  async function trySemanticSearch(ctx, input, filtered, usage) {
@@ -315,7 +340,7 @@ function toHit(loaded, needle, usage) {
315
340
  import { writeFile as writeFile3 } from "fs/promises";
316
341
  import { existsSync as existsSync6 } from "fs";
317
342
  import {
318
- loadMemoriesFromDir as loadMemoriesFromDir3,
343
+ loadMemoriesFromDir as loadMemoriesFromDir4,
319
344
  serializeMemory as serializeMemory2,
320
345
  verifyAnchor
321
346
  } from "@hiveai/core";
@@ -331,7 +356,7 @@ async function memVerify(input, ctx) {
331
356
  summary: { checked: 0, fresh: 0, stale: 0, anchorless_skipped: 0, updated: 0 }
332
357
  };
333
358
  }
334
- const all = await loadMemoriesFromDir3(ctx.paths.memoriesDir);
359
+ const all = await loadMemoriesFromDir4(ctx.paths.memoriesDir);
335
360
  const targets = input.id ? all.filter((m) => m.memory.frontmatter.id === input.id) : all;
336
361
  const results = [];
337
362
  let fresh = 0;
@@ -410,7 +435,7 @@ function applyVerification(mem, result) {
410
435
  import { writeFile as writeFile4 } from "fs/promises";
411
436
  import { existsSync as existsSync7 } from "fs";
412
437
  import {
413
- loadMemoriesFromDir as loadMemoriesFromDir4,
438
+ loadMemoriesFromDir as loadMemoriesFromDir5,
414
439
  loadUsageIndex as loadUsageIndex2,
415
440
  recordRejection,
416
441
  saveUsageIndex,
@@ -425,13 +450,17 @@ async function memReject(input, ctx) {
425
450
  if (!existsSync7(ctx.paths.memoriesDir)) {
426
451
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
427
452
  }
428
- const memories = await loadMemoriesFromDir4(ctx.paths.memoriesDir);
453
+ const memories = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
429
454
  const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
430
455
  if (!loaded) throw new Error(`No memory with id "${input.id}".`);
431
456
  await writeFile4(
432
457
  loaded.filePath,
433
458
  serializeMemory3({
434
- frontmatter: { ...loaded.memory.frontmatter, status: "rejected" },
459
+ frontmatter: {
460
+ ...loaded.memory.frontmatter,
461
+ status: "rejected",
462
+ stale_reason: input.reason ?? loaded.memory.frontmatter.stale_reason ?? null
463
+ },
435
464
  body: loaded.memory.body
436
465
  }),
437
466
  "utf8"
@@ -457,7 +486,7 @@ import {
457
486
  deriveConfidence as deriveConfidence2,
458
487
  getUsage as getUsage2,
459
488
  inferModulesFromPaths,
460
- loadMemoriesFromDir as loadMemoriesFromDir5,
489
+ loadMemoriesFromDir as loadMemoriesFromDir6,
461
490
  loadUsageIndex as loadUsageIndex3,
462
491
  memoryMatchesAnchorPaths,
463
492
  trackReads as trackReads2
@@ -479,7 +508,7 @@ async function memForFiles(input, ctx) {
479
508
  module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
480
509
  };
481
510
  }
482
- const all = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
511
+ const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
483
512
  const usage = await loadUsageIndex3(ctx.paths);
484
513
  const seen = /* @__PURE__ */ new Set();
485
514
  const byAnchor = [];
@@ -558,7 +587,7 @@ import { existsSync as existsSync9 } from "fs";
558
587
  import {
559
588
  deriveConfidence as deriveConfidence3,
560
589
  getUsage as getUsage3,
561
- loadMemoriesFromDir as loadMemoriesFromDir6,
590
+ loadMemoriesFromDir as loadMemoriesFromDir7,
562
591
  loadUsageIndex as loadUsageIndex4
563
592
  } from "@hiveai/core";
564
593
  import { z as z9 } from "zod";
@@ -569,7 +598,7 @@ async function memGet(input, ctx) {
569
598
  if (!existsSync9(ctx.paths.memoriesDir)) {
570
599
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
571
600
  }
572
- const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
601
+ const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
573
602
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
574
603
  if (!found) throw new Error(`No memory with id "${input.id}".`);
575
604
  const fm = found.memory.frontmatter;
@@ -601,7 +630,7 @@ async function memGet(input, ctx) {
601
630
  import { existsSync as existsSync10 } from "fs";
602
631
  import { unlink } from "fs/promises";
603
632
  import {
604
- loadMemoriesFromDir as loadMemoriesFromDir7,
633
+ loadMemoriesFromDir as loadMemoriesFromDir8,
605
634
  loadUsageIndex as loadUsageIndex5,
606
635
  saveUsageIndex as saveUsageIndex2
607
636
  } from "@hiveai/core";
@@ -614,7 +643,7 @@ async function memDelete(input, ctx) {
614
643
  if (!existsSync10(ctx.paths.memoriesDir)) {
615
644
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
616
645
  }
617
- const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
646
+ const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
618
647
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
619
648
  if (!found) throw new Error(`No memory with id "${input.id}".`);
620
649
  await unlink(found.filePath);
@@ -633,7 +662,7 @@ async function memDelete(input, ctx) {
633
662
  // src/tools/mem-update.ts
634
663
  import { writeFile as writeFile5 } from "fs/promises";
635
664
  import { existsSync as existsSync11 } from "fs";
636
- import { loadMemoriesFromDir as loadMemoriesFromDir8, serializeMemory as serializeMemory4 } from "@hiveai/core";
665
+ import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
637
666
  import { z as z11 } from "zod";
638
667
  var MemUpdateInputSchema = {
639
668
  id: z11.string().min(1).describe("Id of the memory to update"),
@@ -649,7 +678,7 @@ async function memUpdate(input, ctx) {
649
678
  if (!existsSync11(ctx.paths.memoriesDir)) {
650
679
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
651
680
  }
652
- const memories = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
681
+ const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
653
682
  const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
654
683
  if (!loaded) throw new Error(`No memory with id "${input.id}".`);
655
684
  const { frontmatter, body } = loaded.memory;
@@ -694,7 +723,7 @@ async function memUpdate(input, ctx) {
694
723
  import { existsSync as existsSync12 } from "fs";
695
724
  import {
696
725
  getUsage as getUsage4,
697
- loadMemoriesFromDir as loadMemoriesFromDir9,
726
+ loadMemoriesFromDir as loadMemoriesFromDir10,
698
727
  loadUsageIndex as loadUsageIndex6
699
728
  } from "@hiveai/core";
700
729
  import { z as z12 } from "zod";
@@ -703,7 +732,7 @@ var MemPendingInputSchema = {
703
732
  };
704
733
  async function memPending(input, ctx) {
705
734
  if (!existsSync12(ctx.paths.memoriesDir)) return { pending: [] };
706
- const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
735
+ const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
707
736
  const usage = await loadUsageIndex6(ctx.paths);
708
737
  const now = Date.now();
709
738
  const proposed = all.filter(({ memory }) => {
@@ -737,7 +766,7 @@ async function memPending(input, ctx) {
737
766
  import { writeFile as writeFile6 } from "fs/promises";
738
767
  import { existsSync as existsSync13 } from "fs";
739
768
  import {
740
- loadMemoriesFromDir as loadMemoriesFromDir10,
769
+ loadMemoriesFromDir as loadMemoriesFromDir11,
741
770
  serializeMemory as serializeMemory5
742
771
  } from "@hiveai/core";
743
772
  import { z as z13 } from "zod";
@@ -748,7 +777,7 @@ async function memApprove(input, ctx) {
748
777
  if (!existsSync13(ctx.paths.memoriesDir)) {
749
778
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
750
779
  }
751
- const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
780
+ const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
752
781
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
753
782
  if (!found) throw new Error(`No memory with id "${input.id}".`);
754
783
  const previous = found.memory.frontmatter.status;
@@ -765,10 +794,60 @@ async function memApprove(input, ctx) {
765
794
  };
766
795
  }
767
796
 
768
- // src/tools/get-briefing.ts
769
- import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
797
+ // src/tools/mem-tried.ts
798
+ import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
770
799
  import { existsSync as existsSync14 } from "fs";
771
800
  import path5 from "path";
801
+ import {
802
+ buildFrontmatter as buildFrontmatter2,
803
+ memoryFilePath as memoryFilePath2,
804
+ serializeMemory as serializeMemory6
805
+ } from "@hiveai/core";
806
+ import { z as z14 } from "zod";
807
+ var MemTriedInputSchema = {
808
+ what: z14.string().min(1).describe("Brief description of the approach that was tried"),
809
+ why_failed: z14.string().min(1).describe("Why it failed or why it should NOT be used"),
810
+ instead: z14.string().optional().describe("What to use or do instead (recommended alternative)"),
811
+ scope: z14.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
812
+ module: z14.string().optional().describe("Module name (required when scope=module)"),
813
+ tags: z14.array(z14.string()).default([]).describe("Tags for filtering"),
814
+ paths: z14.array(z14.string()).default([]).describe("Anchor file paths this applies to"),
815
+ author: z14.string().optional().describe("Author handle or email")
816
+ };
817
+ async function memTried(input, ctx) {
818
+ if (!existsSync14(ctx.paths.haiveDir)) {
819
+ throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
820
+ }
821
+ const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
822
+ const baseFm = buildFrontmatter2({
823
+ type: "attempt",
824
+ slug,
825
+ scope: input.scope,
826
+ module: input.module,
827
+ tags: input.tags,
828
+ paths: input.paths,
829
+ author: input.author
830
+ });
831
+ const frontmatter = { ...baseFm, status: "validated" };
832
+ const lines = [`# ${input.what}`, ""];
833
+ lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
834
+ if (input.instead) {
835
+ lines.push("", `**Instead, use:** ${input.instead}`);
836
+ }
837
+ const body = lines.join("\n") + "\n";
838
+ const file = memoryFilePath2(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
839
+ await mkdir3(path5.dirname(file), { recursive: true });
840
+ if (existsSync14(file)) {
841
+ throw new Error(`Memory already exists at ${file}`);
842
+ }
843
+ await writeFile7(file, serializeMemory6({ frontmatter, body }), "utf8");
844
+ return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
845
+ }
846
+
847
+ // src/tools/get-briefing.ts
848
+ import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
849
+ import { existsSync as existsSync15 } from "fs";
850
+ import path6 from "path";
772
851
  import {
773
852
  allocateBudget,
774
853
  deriveConfidence as deriveConfidence4,
@@ -776,36 +855,43 @@ import {
776
855
  getUsage as getUsage5,
777
856
  inferModulesFromPaths as inferModulesFromPaths2,
778
857
  literalMatchesAllTokens as literalMatchesAllTokens2,
779
- loadMemoriesFromDir as loadMemoriesFromDir11,
858
+ loadMemoriesFromDir as loadMemoriesFromDir12,
780
859
  loadUsageIndex as loadUsageIndex7,
781
860
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
782
861
  tokenizeQuery as tokenizeQuery2,
783
862
  trackReads as trackReads3,
784
863
  truncateToTokens
785
864
  } from "@hiveai/core";
786
- import { z as z14 } from "zod";
865
+ import { z as z15 } from "zod";
787
866
  var GetBriefingInputSchema = {
788
- task: z14.string().optional().describe(
867
+ task: z15.string().optional().describe(
789
868
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
790
869
  ),
791
- files: z14.array(z14.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
792
- max_tokens: z14.number().int().positive().default(8e3).describe(
870
+ files: z15.array(z15.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
871
+ max_tokens: z15.number().int().positive().default(8e3).describe(
793
872
  "Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
794
873
  ),
795
- max_memories: z14.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
796
- include_project_context: z14.boolean().default(true),
797
- include_module_contexts: z14.boolean().default(true),
798
- semantic: z14.boolean().default(true).describe(
874
+ max_memories: z15.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
875
+ include_project_context: z15.boolean().default(true),
876
+ include_module_contexts: z15.boolean().default(true),
877
+ semantic: z15.boolean().default(true).describe(
799
878
  "Use semantic ranking when a task is provided (requires `haive embeddings index`)."
800
879
  ),
801
- track: z14.boolean().default(true).describe("Increment read_count on returned memories")
880
+ include_stale: z15.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
881
+ track: z15.boolean().default(true).describe("Increment read_count on returned memories")
802
882
  };
803
883
  async function getBriefing(input, ctx) {
804
884
  const inferred = inferModulesFromPaths2(input.files);
805
885
  const memories = [];
806
886
  let searchMode = "literal";
807
- if (existsSync14(ctx.paths.memoriesDir)) {
808
- const allMemories = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
887
+ if (existsSync15(ctx.paths.memoriesDir)) {
888
+ const allLoaded = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
889
+ const allMemories = allLoaded.filter(({ memory }) => {
890
+ const s = memory.frontmatter.status;
891
+ if (s === "rejected" || s === "deprecated") return false;
892
+ if (!input.include_stale && s === "stale") return false;
893
+ return true;
894
+ });
809
895
  const usage = await loadUsageIndex7(ctx.paths);
810
896
  const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
811
897
  if (input.task && input.semantic) {
@@ -876,10 +962,13 @@ async function getBriefing(input, ctx) {
876
962
  await trackReads3(ctx.paths, memories.map((m) => m.id));
877
963
  }
878
964
  }
879
- const projectContext = input.include_project_context && existsSync14(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
965
+ const projectContext = input.include_project_context && existsSync15(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
880
966
  const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
881
- const memoriesText = memories.map((m) => `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})
882
- ${m.body.trim()}`).join("\n\n---\n\n");
967
+ const memoriesText = memories.map((m) => {
968
+ const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
969
+ return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
970
+ ${m.body.trim()}`;
971
+ }).join("\n\n---\n\n");
883
972
  const slices = allocateBudget(
884
973
  [
885
974
  { key: "project", text: projectContext, weight: 3, mode: "head" },
@@ -947,15 +1036,15 @@ async function trySemanticHits(ctx, task, limit) {
947
1036
  }
948
1037
  async function loadModuleContexts2(ctx, modules) {
949
1038
  if (modules.length === 0) return [];
950
- if (!existsSync14(ctx.paths.modulesContextDir)) return [];
1039
+ if (!existsSync15(ctx.paths.modulesContextDir)) return [];
951
1040
  const available = new Set(
952
1041
  (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
953
1042
  );
954
1043
  const out = [];
955
1044
  for (const m of modules) {
956
1045
  if (!available.has(m)) continue;
957
- const file = path5.join(ctx.paths.modulesContextDir, m, "context.md");
958
- if (existsSync14(file)) {
1046
+ const file = path6.join(ctx.paths.modulesContextDir, m, "context.md");
1047
+ if (existsSync15(file)) {
959
1048
  out.push({ name: m, content: await readFile3(file, "utf8") });
960
1049
  }
961
1050
  }
@@ -964,11 +1053,11 @@ async function loadModuleContexts2(ctx, modules) {
964
1053
 
965
1054
  // src/tools/code-map.ts
966
1055
  import { loadCodeMap, queryCodeMap } from "@hiveai/core";
967
- import { z as z15 } from "zod";
1056
+ import { z as z16 } from "zod";
968
1057
  var CodeMapInputSchema = {
969
- file: z15.string().optional().describe("Filter to files whose path contains this substring"),
970
- symbol: z15.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
971
- max_files: z15.number().int().positive().default(40).describe("Cap on returned files")
1058
+ file: z16.string().optional().describe("Filter to files whose path contains this substring"),
1059
+ symbol: z16.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
1060
+ max_files: z16.number().int().positive().default(40).describe("Cap on returned files")
972
1061
  };
973
1062
  async function codeMapTool(input, ctx) {
974
1063
  const map = await loadCodeMap(ctx.paths);
@@ -994,12 +1083,12 @@ async function codeMapTool(input, ctx) {
994
1083
  }
995
1084
 
996
1085
  // src/prompts/bootstrap-project.ts
997
- import { z as z16 } from "zod";
1086
+ import { z as z17 } from "zod";
998
1087
  var BootstrapProjectArgsSchema = {
999
- module: z16.string().optional().describe(
1088
+ module: z17.string().optional().describe(
1000
1089
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
1001
1090
  ),
1002
- focus: z16.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
1091
+ focus: z17.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
1003
1092
  };
1004
1093
  var ROOT_TEMPLATE = `# Project context
1005
1094
 
@@ -1082,7 +1171,7 @@ ${template}\`\`\`
1082
1171
 
1083
1172
  // src/server.ts
1084
1173
  var SERVER_NAME = "haive";
1085
- var SERVER_VERSION = "0.2.3";
1174
+ var SERVER_VERSION = "0.2.6";
1086
1175
  function jsonResult(data) {
1087
1176
  return {
1088
1177
  content: [
@@ -1189,6 +1278,12 @@ function createHaiveServer(options = {}) {
1189
1278
  MemApproveInputSchema,
1190
1279
  async (input) => jsonResult(await memApprove(input, context))
1191
1280
  );
1281
+ server.tool(
1282
+ "mem_tried",
1283
+ "Record a failed approach as negative knowledge (type=attempt, auto-validated). Use this whenever you tried something and it didn't work \u2014 saves future agents from repeating the mistake.",
1284
+ MemTriedInputSchema,
1285
+ async (input) => jsonResult(await memTried(input, context))
1286
+ );
1192
1287
  server.prompt(
1193
1288
  "bootstrap_project",
1194
1289
  "Instructions for the AI client to analyze the project and save the context.",