@hiveai/mcp 0.2.4 → 0.2.7
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 +244 -68
- package/dist/index.js.map +1 -1
- package/dist/server.js +244 -68
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
438
|
+
loadMemoriesFromDir as loadMemoriesFromDir5,
|
|
414
439
|
loadUsageIndex as loadUsageIndex2,
|
|
415
440
|
recordRejection,
|
|
416
441
|
saveUsageIndex,
|
|
@@ -425,7 +450,7 @@ 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
|
|
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(
|
|
@@ -461,7 +486,7 @@ import {
|
|
|
461
486
|
deriveConfidence as deriveConfidence2,
|
|
462
487
|
getUsage as getUsage2,
|
|
463
488
|
inferModulesFromPaths,
|
|
464
|
-
loadMemoriesFromDir as
|
|
489
|
+
loadMemoriesFromDir as loadMemoriesFromDir6,
|
|
465
490
|
loadUsageIndex as loadUsageIndex3,
|
|
466
491
|
memoryMatchesAnchorPaths,
|
|
467
492
|
trackReads as trackReads2
|
|
@@ -483,7 +508,7 @@ async function memForFiles(input, ctx) {
|
|
|
483
508
|
module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
|
|
484
509
|
};
|
|
485
510
|
}
|
|
486
|
-
const all = await
|
|
511
|
+
const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
|
|
487
512
|
const usage = await loadUsageIndex3(ctx.paths);
|
|
488
513
|
const seen = /* @__PURE__ */ new Set();
|
|
489
514
|
const byAnchor = [];
|
|
@@ -562,7 +587,7 @@ import { existsSync as existsSync9 } from "fs";
|
|
|
562
587
|
import {
|
|
563
588
|
deriveConfidence as deriveConfidence3,
|
|
564
589
|
getUsage as getUsage3,
|
|
565
|
-
loadMemoriesFromDir as
|
|
590
|
+
loadMemoriesFromDir as loadMemoriesFromDir7,
|
|
566
591
|
loadUsageIndex as loadUsageIndex4
|
|
567
592
|
} from "@hiveai/core";
|
|
568
593
|
import { z as z9 } from "zod";
|
|
@@ -573,7 +598,7 @@ async function memGet(input, ctx) {
|
|
|
573
598
|
if (!existsSync9(ctx.paths.memoriesDir)) {
|
|
574
599
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
575
600
|
}
|
|
576
|
-
const all = await
|
|
601
|
+
const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
|
|
577
602
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
578
603
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
579
604
|
const fm = found.memory.frontmatter;
|
|
@@ -605,7 +630,7 @@ async function memGet(input, ctx) {
|
|
|
605
630
|
import { existsSync as existsSync10 } from "fs";
|
|
606
631
|
import { unlink } from "fs/promises";
|
|
607
632
|
import {
|
|
608
|
-
loadMemoriesFromDir as
|
|
633
|
+
loadMemoriesFromDir as loadMemoriesFromDir8,
|
|
609
634
|
loadUsageIndex as loadUsageIndex5,
|
|
610
635
|
saveUsageIndex as saveUsageIndex2
|
|
611
636
|
} from "@hiveai/core";
|
|
@@ -618,7 +643,7 @@ async function memDelete(input, ctx) {
|
|
|
618
643
|
if (!existsSync10(ctx.paths.memoriesDir)) {
|
|
619
644
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
620
645
|
}
|
|
621
|
-
const all = await
|
|
646
|
+
const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
|
|
622
647
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
623
648
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
624
649
|
await unlink(found.filePath);
|
|
@@ -637,7 +662,7 @@ async function memDelete(input, ctx) {
|
|
|
637
662
|
// src/tools/mem-update.ts
|
|
638
663
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
639
664
|
import { existsSync as existsSync11 } from "fs";
|
|
640
|
-
import { loadMemoriesFromDir as
|
|
665
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
|
|
641
666
|
import { z as z11 } from "zod";
|
|
642
667
|
var MemUpdateInputSchema = {
|
|
643
668
|
id: z11.string().min(1).describe("Id of the memory to update"),
|
|
@@ -653,7 +678,7 @@ async function memUpdate(input, ctx) {
|
|
|
653
678
|
if (!existsSync11(ctx.paths.memoriesDir)) {
|
|
654
679
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
655
680
|
}
|
|
656
|
-
const memories = await
|
|
681
|
+
const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
|
|
657
682
|
const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
|
|
658
683
|
if (!loaded) throw new Error(`No memory with id "${input.id}".`);
|
|
659
684
|
const { frontmatter, body } = loaded.memory;
|
|
@@ -698,7 +723,7 @@ async function memUpdate(input, ctx) {
|
|
|
698
723
|
import { existsSync as existsSync12 } from "fs";
|
|
699
724
|
import {
|
|
700
725
|
getUsage as getUsage4,
|
|
701
|
-
loadMemoriesFromDir as
|
|
726
|
+
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
702
727
|
loadUsageIndex as loadUsageIndex6
|
|
703
728
|
} from "@hiveai/core";
|
|
704
729
|
import { z as z12 } from "zod";
|
|
@@ -707,7 +732,7 @@ var MemPendingInputSchema = {
|
|
|
707
732
|
};
|
|
708
733
|
async function memPending(input, ctx) {
|
|
709
734
|
if (!existsSync12(ctx.paths.memoriesDir)) return { pending: [] };
|
|
710
|
-
const all = await
|
|
735
|
+
const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
|
|
711
736
|
const usage = await loadUsageIndex6(ctx.paths);
|
|
712
737
|
const now = Date.now();
|
|
713
738
|
const proposed = all.filter(({ memory }) => {
|
|
@@ -741,7 +766,7 @@ async function memPending(input, ctx) {
|
|
|
741
766
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
742
767
|
import { existsSync as existsSync13 } from "fs";
|
|
743
768
|
import {
|
|
744
|
-
loadMemoriesFromDir as
|
|
769
|
+
loadMemoriesFromDir as loadMemoriesFromDir11,
|
|
745
770
|
serializeMemory as serializeMemory5
|
|
746
771
|
} from "@hiveai/core";
|
|
747
772
|
import { z as z13 } from "zod";
|
|
@@ -752,7 +777,7 @@ async function memApprove(input, ctx) {
|
|
|
752
777
|
if (!existsSync13(ctx.paths.memoriesDir)) {
|
|
753
778
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
754
779
|
}
|
|
755
|
-
const all = await
|
|
780
|
+
const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
|
|
756
781
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
757
782
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
758
783
|
const previous = found.memory.frontmatter.status;
|
|
@@ -769,10 +794,60 @@ async function memApprove(input, ctx) {
|
|
|
769
794
|
};
|
|
770
795
|
}
|
|
771
796
|
|
|
772
|
-
// src/tools/
|
|
773
|
-
import {
|
|
797
|
+
// src/tools/mem-tried.ts
|
|
798
|
+
import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
|
|
774
799
|
import { existsSync as existsSync14 } from "fs";
|
|
775
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";
|
|
776
851
|
import {
|
|
777
852
|
allocateBudget,
|
|
778
853
|
deriveConfidence as deriveConfidence4,
|
|
@@ -780,43 +855,50 @@ import {
|
|
|
780
855
|
getUsage as getUsage5,
|
|
781
856
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
782
857
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
783
|
-
loadMemoriesFromDir as
|
|
858
|
+
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
784
859
|
loadUsageIndex as loadUsageIndex7,
|
|
785
860
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
786
861
|
tokenizeQuery as tokenizeQuery2,
|
|
787
862
|
trackReads as trackReads3,
|
|
788
863
|
truncateToTokens
|
|
789
864
|
} from "@hiveai/core";
|
|
790
|
-
import { z as
|
|
865
|
+
import { z as z15 } from "zod";
|
|
791
866
|
var GetBriefingInputSchema = {
|
|
792
|
-
task:
|
|
867
|
+
task: z15.string().optional().describe(
|
|
793
868
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
794
869
|
),
|
|
795
|
-
files:
|
|
796
|
-
max_tokens:
|
|
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(
|
|
797
872
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
798
873
|
),
|
|
799
|
-
max_memories:
|
|
800
|
-
include_project_context:
|
|
801
|
-
include_module_contexts:
|
|
802
|
-
semantic:
|
|
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(
|
|
803
878
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
804
879
|
),
|
|
805
|
-
|
|
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")
|
|
806
882
|
};
|
|
807
883
|
async function getBriefing(input, ctx) {
|
|
808
884
|
const inferred = inferModulesFromPaths2(input.files);
|
|
809
885
|
const memories = [];
|
|
810
886
|
let searchMode = "literal";
|
|
811
|
-
if (
|
|
812
|
-
const
|
|
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
|
+
});
|
|
813
895
|
const usage = await loadUsageIndex7(ctx.paths);
|
|
814
896
|
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
815
897
|
if (input.task && input.semantic) {
|
|
816
898
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
817
899
|
}
|
|
818
900
|
const seen = /* @__PURE__ */ new Map();
|
|
819
|
-
const addOrUpdate = (loaded, reason, score) => {
|
|
901
|
+
const addOrUpdate = (loaded, reason, score, matchQuality) => {
|
|
820
902
|
const fm = loaded.memory.frontmatter;
|
|
821
903
|
const existing = seen.get(fm.id);
|
|
822
904
|
if (existing) {
|
|
@@ -824,6 +906,11 @@ async function getBriefing(input, ctx) {
|
|
|
824
906
|
if (score !== void 0 && (existing.semantic_score ?? 0) < score) {
|
|
825
907
|
existing.semantic_score = score;
|
|
826
908
|
}
|
|
909
|
+
if (matchQuality === "exact" && existing.match_quality !== "exact") {
|
|
910
|
+
existing.match_quality = "exact";
|
|
911
|
+
} else if (matchQuality === "semantic" && existing.match_quality === "partial") {
|
|
912
|
+
existing.match_quality = "semantic";
|
|
913
|
+
}
|
|
827
914
|
return;
|
|
828
915
|
}
|
|
829
916
|
const u = getUsage5(usage, fm.id);
|
|
@@ -837,6 +924,7 @@ async function getBriefing(input, ctx) {
|
|
|
837
924
|
confidence: deriveConfidence4(fm, u),
|
|
838
925
|
read_count: u.read_count,
|
|
839
926
|
reasons: [reason],
|
|
927
|
+
match_quality: matchQuality ?? "partial",
|
|
840
928
|
...score !== void 0 ? { semantic_score: score } : {},
|
|
841
929
|
body: loaded.memory.body,
|
|
842
930
|
file_path: loaded.filePath
|
|
@@ -844,32 +932,33 @@ async function getBriefing(input, ctx) {
|
|
|
844
932
|
};
|
|
845
933
|
if (input.files.length > 0) {
|
|
846
934
|
for (const loaded of allMemories) {
|
|
847
|
-
if (memoryMatchesAnchorPaths2(loaded.memory, input.files)) addOrUpdate(loaded, "anchor");
|
|
935
|
+
if (memoryMatchesAnchorPaths2(loaded.memory, input.files)) addOrUpdate(loaded, "anchor", void 0, "exact");
|
|
848
936
|
}
|
|
849
937
|
for (const loaded of allMemories) {
|
|
850
938
|
const fm = loaded.memory.frontmatter;
|
|
851
|
-
if (fm.module && inferred.includes(fm.module)) addOrUpdate(loaded, "module");
|
|
852
|
-
if (fm.domain && inferred.includes(fm.domain)) addOrUpdate(loaded, "domain");
|
|
853
|
-
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module");
|
|
939
|
+
if (fm.module && inferred.includes(fm.module)) addOrUpdate(loaded, "module", void 0, "partial");
|
|
940
|
+
if (fm.domain && inferred.includes(fm.domain)) addOrUpdate(loaded, "domain", void 0, "partial");
|
|
941
|
+
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module", void 0, "partial");
|
|
854
942
|
}
|
|
855
943
|
}
|
|
856
944
|
if (input.task) {
|
|
857
945
|
const tokens = tokenizeQuery2(input.task);
|
|
858
946
|
for (const loaded of allMemories) {
|
|
859
947
|
if (literalMatchesAllTokens2(loaded.memory, tokens)) {
|
|
860
|
-
addOrUpdate(loaded, "semantic");
|
|
948
|
+
addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
861
949
|
}
|
|
862
950
|
}
|
|
863
951
|
if (semanticHits) {
|
|
864
952
|
const byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
865
953
|
for (const hit of semanticHits) {
|
|
866
954
|
const loaded = byId.get(hit.id);
|
|
867
|
-
if (loaded) addOrUpdate(loaded, "semantic", hit.score);
|
|
955
|
+
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
868
956
|
}
|
|
869
957
|
}
|
|
870
958
|
}
|
|
871
959
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
872
|
-
const reasonScore = (m) => (m.
|
|
960
|
+
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
|
|
961
|
+
(m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
873
962
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
874
963
|
const sa = reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
875
964
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
@@ -880,10 +969,13 @@ async function getBriefing(input, ctx) {
|
|
|
880
969
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
881
970
|
}
|
|
882
971
|
}
|
|
883
|
-
const projectContext = input.include_project_context &&
|
|
972
|
+
const projectContext = input.include_project_context && existsSync15(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
884
973
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
885
|
-
const memoriesText = memories.map((m) =>
|
|
886
|
-
|
|
974
|
+
const memoriesText = memories.map((m) => {
|
|
975
|
+
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
976
|
+
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
977
|
+
${m.body.trim()}`;
|
|
978
|
+
}).join("\n\n---\n\n");
|
|
887
979
|
const slices = allocateBudget(
|
|
888
980
|
[
|
|
889
981
|
{ key: "project", text: projectContext, weight: 3, mode: "head" },
|
|
@@ -913,12 +1005,24 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
913
1005
|
}
|
|
914
1006
|
}
|
|
915
1007
|
const trimmedMemoriesText = memoriesSlice.text;
|
|
916
|
-
const trimmedMemories =
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1008
|
+
const trimmedMemories = [];
|
|
1009
|
+
if (!memoriesSlice.truncated) {
|
|
1010
|
+
trimmedMemories.push(...memories);
|
|
1011
|
+
} else {
|
|
1012
|
+
let remaining = memoriesSlice.allocatedTokens;
|
|
1013
|
+
for (const m of memories) {
|
|
1014
|
+
const bodyTokens = estimateTokens(m.body);
|
|
1015
|
+
if (remaining <= 0) break;
|
|
1016
|
+
if (bodyTokens <= remaining) {
|
|
1017
|
+
trimmedMemories.push(m);
|
|
1018
|
+
remaining -= bodyTokens;
|
|
1019
|
+
} else if (remaining > 80) {
|
|
1020
|
+
const t = truncateToTokens(m.body, { maxTokens: remaining, mode: "head" });
|
|
1021
|
+
trimmedMemories.push({ ...m, body: t.text });
|
|
1022
|
+
remaining = 0;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
922
1026
|
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
923
1027
|
return {
|
|
924
1028
|
...input.task ? { task: input.task } : {},
|
|
@@ -951,15 +1055,15 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
951
1055
|
}
|
|
952
1056
|
async function loadModuleContexts2(ctx, modules) {
|
|
953
1057
|
if (modules.length === 0) return [];
|
|
954
|
-
if (!
|
|
1058
|
+
if (!existsSync15(ctx.paths.modulesContextDir)) return [];
|
|
955
1059
|
const available = new Set(
|
|
956
1060
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
957
1061
|
);
|
|
958
1062
|
const out = [];
|
|
959
1063
|
for (const m of modules) {
|
|
960
1064
|
if (!available.has(m)) continue;
|
|
961
|
-
const file =
|
|
962
|
-
if (
|
|
1065
|
+
const file = path6.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1066
|
+
if (existsSync15(file)) {
|
|
963
1067
|
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
964
1068
|
}
|
|
965
1069
|
}
|
|
@@ -968,11 +1072,11 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
968
1072
|
|
|
969
1073
|
// src/tools/code-map.ts
|
|
970
1074
|
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
971
|
-
import { z as
|
|
1075
|
+
import { z as z16 } from "zod";
|
|
972
1076
|
var CodeMapInputSchema = {
|
|
973
|
-
file:
|
|
974
|
-
symbol:
|
|
975
|
-
max_files:
|
|
1077
|
+
file: z16.string().optional().describe("Filter to files whose path contains this substring"),
|
|
1078
|
+
symbol: z16.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
1079
|
+
max_files: z16.number().int().positive().default(40).describe("Cap on returned files")
|
|
976
1080
|
};
|
|
977
1081
|
async function codeMapTool(input, ctx) {
|
|
978
1082
|
const map = await loadCodeMap(ctx.paths);
|
|
@@ -998,12 +1102,12 @@ async function codeMapTool(input, ctx) {
|
|
|
998
1102
|
}
|
|
999
1103
|
|
|
1000
1104
|
// src/prompts/bootstrap-project.ts
|
|
1001
|
-
import { z as
|
|
1105
|
+
import { z as z17 } from "zod";
|
|
1002
1106
|
var BootstrapProjectArgsSchema = {
|
|
1003
|
-
module:
|
|
1107
|
+
module: z17.string().optional().describe(
|
|
1004
1108
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1005
1109
|
),
|
|
1006
|
-
focus:
|
|
1110
|
+
focus: z17.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1007
1111
|
};
|
|
1008
1112
|
var ROOT_TEMPLATE = `# Project context
|
|
1009
1113
|
|
|
@@ -1084,9 +1188,69 @@ ${template}\`\`\`
|
|
|
1084
1188
|
};
|
|
1085
1189
|
}
|
|
1086
1190
|
|
|
1191
|
+
// src/prompts/post-task.ts
|
|
1192
|
+
import { z as z18 } from "zod";
|
|
1193
|
+
var PostTaskArgsSchema = {
|
|
1194
|
+
task_summary: z18.string().optional().describe("One sentence describing what you just did"),
|
|
1195
|
+
files_touched: z18.array(z18.string()).optional().describe("Files you created or modified during the task")
|
|
1196
|
+
};
|
|
1197
|
+
function postTaskPrompt(args, ctx) {
|
|
1198
|
+
const taskLine = args.task_summary ? `
|
|
1199
|
+
Task just completed: **${args.task_summary}**` : "";
|
|
1200
|
+
const filesLine = args.files_touched && args.files_touched.length > 0 ? `
|
|
1201
|
+
Files touched: ${args.files_touched.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
1202
|
+
const text = `You have just finished a task. Before closing this session, take 60 seconds to capture what you learned.
|
|
1203
|
+
${taskLine}${filesLine}
|
|
1204
|
+
|
|
1205
|
+
Project root: \`${ctx.paths.root}\`
|
|
1206
|
+
|
|
1207
|
+
## Checklist \u2014 answer each question honestly
|
|
1208
|
+
|
|
1209
|
+
Go through each item. If the answer is yes, call the corresponding tool immediately.
|
|
1210
|
+
|
|
1211
|
+
### 1. Did you try an approach that failed?
|
|
1212
|
+
\u2192 If yes, call **\`mem_tried\`** with:
|
|
1213
|
+
- \`what\`: the approach you tried (e.g. "importing gray-matter with ESM dynamic import")
|
|
1214
|
+
- \`why_failed\`: why it didn't work
|
|
1215
|
+
- \`instead\`: what worked instead
|
|
1216
|
+
- \`scope\`: "team" if others will hit the same issue, "personal" if specific to your setup
|
|
1217
|
+
- \`paths\`: the files where the issue manifested
|
|
1218
|
+
|
|
1219
|
+
### 2. Did you discover a convention that isn't documented?
|
|
1220
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="convention"\` and \`scope="team"\`
|
|
1221
|
+
|
|
1222
|
+
### 3. Did you make an architectural decision?
|
|
1223
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="decision"\` and document the WHY (constraints, tradeoffs), not just the what
|
|
1224
|
+
|
|
1225
|
+
### 4. Did you hit a non-obvious bug or surprising behavior?
|
|
1226
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="gotcha"\` and anchor it to the relevant file paths
|
|
1227
|
+
|
|
1228
|
+
### 5. Did you find that an existing memory is outdated or wrong?
|
|
1229
|
+
\u2192 If yes, call **\`mem_update\`** with the correct information, or **\`mem_reject\`** if it's completely wrong
|
|
1230
|
+
|
|
1231
|
+
## Rules
|
|
1232
|
+
|
|
1233
|
+
- One memory per insight. Don't cram multiple lessons into one body.
|
|
1234
|
+
- Anchor memories to file paths when possible (the \`paths\` field) \u2014 this enables staleness detection.
|
|
1235
|
+
- Prefer \`scope="team"\` for anything a teammate or future agent would benefit from.
|
|
1236
|
+
- Skip sections where you genuinely have nothing to add. Don't fabricate memories.
|
|
1237
|
+
|
|
1238
|
+
When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "Nothing new to save."
|
|
1239
|
+
`;
|
|
1240
|
+
return {
|
|
1241
|
+
description: "Post-task reflection: capture what you learned before closing the session",
|
|
1242
|
+
messages: [
|
|
1243
|
+
{
|
|
1244
|
+
role: "user",
|
|
1245
|
+
content: { type: "text", text }
|
|
1246
|
+
}
|
|
1247
|
+
]
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1087
1251
|
// src/server.ts
|
|
1088
1252
|
var SERVER_NAME = "haive";
|
|
1089
|
-
var SERVER_VERSION = "0.2.
|
|
1253
|
+
var SERVER_VERSION = "0.2.7";
|
|
1090
1254
|
function jsonResult(data) {
|
|
1091
1255
|
return {
|
|
1092
1256
|
content: [
|
|
@@ -1105,7 +1269,7 @@ function createHaiveServer(options = {}) {
|
|
|
1105
1269
|
);
|
|
1106
1270
|
server.tool(
|
|
1107
1271
|
"mem_save",
|
|
1108
|
-
"Save a new memory (
|
|
1272
|
+
"Save a new memory (convention, decision, gotcha, architecture, glossary). For failed approaches use mem_tried instead \u2014 it enforces a structured format that is more useful to future agents. Use scope=team to share with the whole team.",
|
|
1109
1273
|
MemSaveInputSchema,
|
|
1110
1274
|
async (input) => jsonResult(await memSave(input, context))
|
|
1111
1275
|
);
|
|
@@ -1193,12 +1357,24 @@ function createHaiveServer(options = {}) {
|
|
|
1193
1357
|
MemApproveInputSchema,
|
|
1194
1358
|
async (input) => jsonResult(await memApprove(input, context))
|
|
1195
1359
|
);
|
|
1360
|
+
server.tool(
|
|
1361
|
+
"mem_tried",
|
|
1362
|
+
"Preferred way to record a failed approach. Enforces a structured what/why_failed/instead format that is immediately actionable for future agents. Auto-validated (no approval cycle). Use whenever you tried an approach and it failed \u2014 prevents the same mistake from happening in the next session.",
|
|
1363
|
+
MemTriedInputSchema,
|
|
1364
|
+
async (input) => jsonResult(await memTried(input, context))
|
|
1365
|
+
);
|
|
1196
1366
|
server.prompt(
|
|
1197
1367
|
"bootstrap_project",
|
|
1198
1368
|
"Instructions for the AI client to analyze the project and save the context.",
|
|
1199
1369
|
BootstrapProjectArgsSchema,
|
|
1200
1370
|
(args) => bootstrapProjectPrompt(args, context)
|
|
1201
1371
|
);
|
|
1372
|
+
server.prompt(
|
|
1373
|
+
"post_task",
|
|
1374
|
+
"Post-task checklist: run this after completing a task to capture failed approaches, new conventions, decisions, and gotchas before closing the session.",
|
|
1375
|
+
PostTaskArgsSchema,
|
|
1376
|
+
(args) => postTaskPrompt(args, context)
|
|
1377
|
+
);
|
|
1202
1378
|
return { server, context };
|
|
1203
1379
|
}
|
|
1204
1380
|
export {
|