@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/index.js
CHANGED
|
@@ -129,12 +129,13 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
129
129
|
import path3 from "path";
|
|
130
130
|
import {
|
|
131
131
|
buildFrontmatter,
|
|
132
|
+
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
132
133
|
memoryFilePath,
|
|
133
134
|
serializeMemory
|
|
134
135
|
} from "@hiveai/core";
|
|
135
136
|
import { z as z4 } from "zod";
|
|
136
137
|
var MemSaveInputSchema = {
|
|
137
|
-
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary"]).describe("Kind of memory being saved"),
|
|
138
|
+
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).describe("Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated)."),
|
|
138
139
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
139
140
|
body: z4.string().describe("Markdown body of the memory"),
|
|
140
141
|
scope: z4.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope: personal | team | module"),
|
|
@@ -174,11 +175,24 @@ async function memSave(input, ctx) {
|
|
|
174
175
|
if (existsSync4(file)) {
|
|
175
176
|
throw new Error(`Memory already exists at ${file}`);
|
|
176
177
|
}
|
|
178
|
+
let warning;
|
|
179
|
+
if (existsSync4(ctx.paths.memoriesDir)) {
|
|
180
|
+
const existing = await loadMemoriesFromDir2(ctx.paths.memoriesDir);
|
|
181
|
+
const slugTokens = input.slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
182
|
+
const similar = existing.filter(({ memory }) => {
|
|
183
|
+
const id = memory.frontmatter.id.toLowerCase();
|
|
184
|
+
return slugTokens.length >= 2 && slugTokens.filter((t) => id.includes(t)).length >= Math.ceil(slugTokens.length * 0.6);
|
|
185
|
+
});
|
|
186
|
+
if (similar.length > 0) {
|
|
187
|
+
warning = `Possible duplicate detected. Similar memories: ${similar.map((m) => m.memory.frontmatter.id).join(", ")}. Consider updating one of these instead.`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
177
190
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
178
191
|
return {
|
|
179
192
|
id: frontmatter.id,
|
|
180
193
|
scope: frontmatter.scope,
|
|
181
|
-
file_path: file
|
|
194
|
+
file_path: file,
|
|
195
|
+
...warning ? { warning } : {}
|
|
182
196
|
};
|
|
183
197
|
}
|
|
184
198
|
|
|
@@ -189,7 +203,8 @@ import {
|
|
|
189
203
|
extractSnippet,
|
|
190
204
|
getUsage,
|
|
191
205
|
literalMatchesAllTokens,
|
|
192
|
-
|
|
206
|
+
literalMatchesAnyToken,
|
|
207
|
+
loadMemoriesFromDir as loadMemoriesFromDir3,
|
|
193
208
|
loadUsageIndex,
|
|
194
209
|
pickSnippetNeedle,
|
|
195
210
|
tokenizeQuery,
|
|
@@ -199,7 +214,7 @@ import { z as z5 } from "zod";
|
|
|
199
214
|
var MemSearchInputSchema = {
|
|
200
215
|
query: z5.string().describe("Substring matched against id, tags, and body"),
|
|
201
216
|
scope: z5.enum(["personal", "team", "module"]).optional().describe("Restrict results to a single scope"),
|
|
202
|
-
type: z5.enum(["convention", "decision", "gotcha", "architecture", "glossary"]).optional().describe("Restrict results to a memory type"),
|
|
217
|
+
type: z5.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).optional().describe("Restrict results to a memory type"),
|
|
203
218
|
module: z5.string().optional().describe("Restrict results to a module"),
|
|
204
219
|
status: z5.enum(["draft", "proposed", "validated", "deprecated", "stale", "rejected"]).optional().describe("Filter by a single status. Omit to return all statuses."),
|
|
205
220
|
exclude_rejected: z5.boolean().default(false).describe("When true, exclude memories with status=rejected from results."),
|
|
@@ -214,7 +229,7 @@ async function memSearch(input, ctx) {
|
|
|
214
229
|
if (!existsSync5(ctx.paths.memoriesDir)) {
|
|
215
230
|
return { matches: [], total: 0, mode: input.semantic ? "literal_fallback" : "literal" };
|
|
216
231
|
}
|
|
217
|
-
const all = await
|
|
232
|
+
const all = await loadMemoriesFromDir3(ctx.paths.memoriesDir);
|
|
218
233
|
const filtered = all.filter(({ memory }) => passesFilters(memory.frontmatter, input));
|
|
219
234
|
const usage = await loadUsageIndex(ctx.paths);
|
|
220
235
|
let result;
|
|
@@ -250,13 +265,23 @@ function passesFilters(fm, input) {
|
|
|
250
265
|
}
|
|
251
266
|
function buildLiteralResult(input, filtered, usage) {
|
|
252
267
|
const tokens = tokenizeQuery(input.query);
|
|
253
|
-
const matched = filtered.filter(({ memory }) => literalMatchesAllTokens(memory, tokens));
|
|
254
268
|
const snippetNeedle = pickSnippetNeedle(input.query);
|
|
255
|
-
|
|
269
|
+
let andMatched = filtered.filter(({ memory }) => literalMatchesAllTokens(memory, tokens));
|
|
270
|
+
if (andMatched.length > 0) {
|
|
271
|
+
const top2 = andMatched.slice(0, input.limit);
|
|
272
|
+
return {
|
|
273
|
+
matches: top2.map((loaded) => toHit(loaded, snippetNeedle, usage)),
|
|
274
|
+
total: andMatched.length,
|
|
275
|
+
mode: "literal"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const orMatched = filtered.filter(({ memory }) => literalMatchesAnyToken(memory, tokens));
|
|
279
|
+
const top = orMatched.slice(0, input.limit);
|
|
256
280
|
return {
|
|
257
281
|
matches: top.map((loaded) => toHit(loaded, snippetNeedle, usage)),
|
|
258
|
-
total:
|
|
259
|
-
mode: "literal"
|
|
282
|
+
total: orMatched.length,
|
|
283
|
+
mode: "literal",
|
|
284
|
+
notice: `No exact match for all tokens. Showing partial matches (OR fallback) \u2014 ${orMatched.length} result${orMatched.length === 1 ? "" : "s"}.`
|
|
260
285
|
};
|
|
261
286
|
}
|
|
262
287
|
async function trySemanticSearch(ctx, input, filtered, usage) {
|
|
@@ -320,7 +345,7 @@ function toHit(loaded, needle, usage) {
|
|
|
320
345
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
321
346
|
import { existsSync as existsSync6 } from "fs";
|
|
322
347
|
import {
|
|
323
|
-
loadMemoriesFromDir as
|
|
348
|
+
loadMemoriesFromDir as loadMemoriesFromDir4,
|
|
324
349
|
serializeMemory as serializeMemory2,
|
|
325
350
|
verifyAnchor
|
|
326
351
|
} from "@hiveai/core";
|
|
@@ -336,7 +361,7 @@ async function memVerify(input, ctx) {
|
|
|
336
361
|
summary: { checked: 0, fresh: 0, stale: 0, anchorless_skipped: 0, updated: 0 }
|
|
337
362
|
};
|
|
338
363
|
}
|
|
339
|
-
const all = await
|
|
364
|
+
const all = await loadMemoriesFromDir4(ctx.paths.memoriesDir);
|
|
340
365
|
const targets = input.id ? all.filter((m) => m.memory.frontmatter.id === input.id) : all;
|
|
341
366
|
const results = [];
|
|
342
367
|
let fresh = 0;
|
|
@@ -415,7 +440,7 @@ function applyVerification(mem, result) {
|
|
|
415
440
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
416
441
|
import { existsSync as existsSync7 } from "fs";
|
|
417
442
|
import {
|
|
418
|
-
loadMemoriesFromDir as
|
|
443
|
+
loadMemoriesFromDir as loadMemoriesFromDir5,
|
|
419
444
|
loadUsageIndex as loadUsageIndex2,
|
|
420
445
|
recordRejection,
|
|
421
446
|
saveUsageIndex,
|
|
@@ -430,7 +455,7 @@ async function memReject(input, ctx) {
|
|
|
430
455
|
if (!existsSync7(ctx.paths.memoriesDir)) {
|
|
431
456
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
432
457
|
}
|
|
433
|
-
const memories = await
|
|
458
|
+
const memories = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
|
|
434
459
|
const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
|
|
435
460
|
if (!loaded) throw new Error(`No memory with id "${input.id}".`);
|
|
436
461
|
await writeFile4(
|
|
@@ -466,7 +491,7 @@ import {
|
|
|
466
491
|
deriveConfidence as deriveConfidence2,
|
|
467
492
|
getUsage as getUsage2,
|
|
468
493
|
inferModulesFromPaths,
|
|
469
|
-
loadMemoriesFromDir as
|
|
494
|
+
loadMemoriesFromDir as loadMemoriesFromDir6,
|
|
470
495
|
loadUsageIndex as loadUsageIndex3,
|
|
471
496
|
memoryMatchesAnchorPaths,
|
|
472
497
|
trackReads as trackReads2
|
|
@@ -488,7 +513,7 @@ async function memForFiles(input, ctx) {
|
|
|
488
513
|
module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
|
|
489
514
|
};
|
|
490
515
|
}
|
|
491
|
-
const all = await
|
|
516
|
+
const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
|
|
492
517
|
const usage = await loadUsageIndex3(ctx.paths);
|
|
493
518
|
const seen = /* @__PURE__ */ new Set();
|
|
494
519
|
const byAnchor = [];
|
|
@@ -567,7 +592,7 @@ import { existsSync as existsSync9 } from "fs";
|
|
|
567
592
|
import {
|
|
568
593
|
deriveConfidence as deriveConfidence3,
|
|
569
594
|
getUsage as getUsage3,
|
|
570
|
-
loadMemoriesFromDir as
|
|
595
|
+
loadMemoriesFromDir as loadMemoriesFromDir7,
|
|
571
596
|
loadUsageIndex as loadUsageIndex4
|
|
572
597
|
} from "@hiveai/core";
|
|
573
598
|
import { z as z9 } from "zod";
|
|
@@ -578,7 +603,7 @@ async function memGet(input, ctx) {
|
|
|
578
603
|
if (!existsSync9(ctx.paths.memoriesDir)) {
|
|
579
604
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
580
605
|
}
|
|
581
|
-
const all = await
|
|
606
|
+
const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
|
|
582
607
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
583
608
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
584
609
|
const fm = found.memory.frontmatter;
|
|
@@ -610,7 +635,7 @@ async function memGet(input, ctx) {
|
|
|
610
635
|
import { existsSync as existsSync10 } from "fs";
|
|
611
636
|
import { unlink } from "fs/promises";
|
|
612
637
|
import {
|
|
613
|
-
loadMemoriesFromDir as
|
|
638
|
+
loadMemoriesFromDir as loadMemoriesFromDir8,
|
|
614
639
|
loadUsageIndex as loadUsageIndex5,
|
|
615
640
|
saveUsageIndex as saveUsageIndex2
|
|
616
641
|
} from "@hiveai/core";
|
|
@@ -623,7 +648,7 @@ async function memDelete(input, ctx) {
|
|
|
623
648
|
if (!existsSync10(ctx.paths.memoriesDir)) {
|
|
624
649
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
625
650
|
}
|
|
626
|
-
const all = await
|
|
651
|
+
const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
|
|
627
652
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
628
653
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
629
654
|
await unlink(found.filePath);
|
|
@@ -642,7 +667,7 @@ async function memDelete(input, ctx) {
|
|
|
642
667
|
// src/tools/mem-update.ts
|
|
643
668
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
644
669
|
import { existsSync as existsSync11 } from "fs";
|
|
645
|
-
import { loadMemoriesFromDir as
|
|
670
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
|
|
646
671
|
import { z as z11 } from "zod";
|
|
647
672
|
var MemUpdateInputSchema = {
|
|
648
673
|
id: z11.string().min(1).describe("Id of the memory to update"),
|
|
@@ -658,7 +683,7 @@ async function memUpdate(input, ctx) {
|
|
|
658
683
|
if (!existsSync11(ctx.paths.memoriesDir)) {
|
|
659
684
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
660
685
|
}
|
|
661
|
-
const memories = await
|
|
686
|
+
const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
|
|
662
687
|
const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
|
|
663
688
|
if (!loaded) throw new Error(`No memory with id "${input.id}".`);
|
|
664
689
|
const { frontmatter, body } = loaded.memory;
|
|
@@ -703,7 +728,7 @@ async function memUpdate(input, ctx) {
|
|
|
703
728
|
import { existsSync as existsSync12 } from "fs";
|
|
704
729
|
import {
|
|
705
730
|
getUsage as getUsage4,
|
|
706
|
-
loadMemoriesFromDir as
|
|
731
|
+
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
707
732
|
loadUsageIndex as loadUsageIndex6
|
|
708
733
|
} from "@hiveai/core";
|
|
709
734
|
import { z as z12 } from "zod";
|
|
@@ -712,7 +737,7 @@ var MemPendingInputSchema = {
|
|
|
712
737
|
};
|
|
713
738
|
async function memPending(input, ctx) {
|
|
714
739
|
if (!existsSync12(ctx.paths.memoriesDir)) return { pending: [] };
|
|
715
|
-
const all = await
|
|
740
|
+
const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
|
|
716
741
|
const usage = await loadUsageIndex6(ctx.paths);
|
|
717
742
|
const now = Date.now();
|
|
718
743
|
const proposed = all.filter(({ memory }) => {
|
|
@@ -746,7 +771,7 @@ async function memPending(input, ctx) {
|
|
|
746
771
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
747
772
|
import { existsSync as existsSync13 } from "fs";
|
|
748
773
|
import {
|
|
749
|
-
loadMemoriesFromDir as
|
|
774
|
+
loadMemoriesFromDir as loadMemoriesFromDir11,
|
|
750
775
|
serializeMemory as serializeMemory5
|
|
751
776
|
} from "@hiveai/core";
|
|
752
777
|
import { z as z13 } from "zod";
|
|
@@ -757,7 +782,7 @@ async function memApprove(input, ctx) {
|
|
|
757
782
|
if (!existsSync13(ctx.paths.memoriesDir)) {
|
|
758
783
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
759
784
|
}
|
|
760
|
-
const all = await
|
|
785
|
+
const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
|
|
761
786
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
762
787
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
763
788
|
const previous = found.memory.frontmatter.status;
|
|
@@ -774,10 +799,60 @@ async function memApprove(input, ctx) {
|
|
|
774
799
|
};
|
|
775
800
|
}
|
|
776
801
|
|
|
777
|
-
// src/tools/
|
|
778
|
-
import {
|
|
802
|
+
// src/tools/mem-tried.ts
|
|
803
|
+
import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
|
|
779
804
|
import { existsSync as existsSync14 } from "fs";
|
|
780
805
|
import path5 from "path";
|
|
806
|
+
import {
|
|
807
|
+
buildFrontmatter as buildFrontmatter2,
|
|
808
|
+
memoryFilePath as memoryFilePath2,
|
|
809
|
+
serializeMemory as serializeMemory6
|
|
810
|
+
} from "@hiveai/core";
|
|
811
|
+
import { z as z14 } from "zod";
|
|
812
|
+
var MemTriedInputSchema = {
|
|
813
|
+
what: z14.string().min(1).describe("Brief description of the approach that was tried"),
|
|
814
|
+
why_failed: z14.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
815
|
+
instead: z14.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
816
|
+
scope: z14.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
|
|
817
|
+
module: z14.string().optional().describe("Module name (required when scope=module)"),
|
|
818
|
+
tags: z14.array(z14.string()).default([]).describe("Tags for filtering"),
|
|
819
|
+
paths: z14.array(z14.string()).default([]).describe("Anchor file paths this applies to"),
|
|
820
|
+
author: z14.string().optional().describe("Author handle or email")
|
|
821
|
+
};
|
|
822
|
+
async function memTried(input, ctx) {
|
|
823
|
+
if (!existsSync14(ctx.paths.haiveDir)) {
|
|
824
|
+
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
825
|
+
}
|
|
826
|
+
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
827
|
+
const baseFm = buildFrontmatter2({
|
|
828
|
+
type: "attempt",
|
|
829
|
+
slug,
|
|
830
|
+
scope: input.scope,
|
|
831
|
+
module: input.module,
|
|
832
|
+
tags: input.tags,
|
|
833
|
+
paths: input.paths,
|
|
834
|
+
author: input.author
|
|
835
|
+
});
|
|
836
|
+
const frontmatter = { ...baseFm, status: "validated" };
|
|
837
|
+
const lines = [`# ${input.what}`, ""];
|
|
838
|
+
lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
|
|
839
|
+
if (input.instead) {
|
|
840
|
+
lines.push("", `**Instead, use:** ${input.instead}`);
|
|
841
|
+
}
|
|
842
|
+
const body = lines.join("\n") + "\n";
|
|
843
|
+
const file = memoryFilePath2(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
844
|
+
await mkdir3(path5.dirname(file), { recursive: true });
|
|
845
|
+
if (existsSync14(file)) {
|
|
846
|
+
throw new Error(`Memory already exists at ${file}`);
|
|
847
|
+
}
|
|
848
|
+
await writeFile7(file, serializeMemory6({ frontmatter, body }), "utf8");
|
|
849
|
+
return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/tools/get-briefing.ts
|
|
853
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
854
|
+
import { existsSync as existsSync15 } from "fs";
|
|
855
|
+
import path6 from "path";
|
|
781
856
|
import {
|
|
782
857
|
allocateBudget,
|
|
783
858
|
deriveConfidence as deriveConfidence4,
|
|
@@ -785,43 +860,50 @@ import {
|
|
|
785
860
|
getUsage as getUsage5,
|
|
786
861
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
787
862
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
788
|
-
loadMemoriesFromDir as
|
|
863
|
+
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
789
864
|
loadUsageIndex as loadUsageIndex7,
|
|
790
865
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
791
866
|
tokenizeQuery as tokenizeQuery2,
|
|
792
867
|
trackReads as trackReads3,
|
|
793
868
|
truncateToTokens
|
|
794
869
|
} from "@hiveai/core";
|
|
795
|
-
import { z as
|
|
870
|
+
import { z as z15 } from "zod";
|
|
796
871
|
var GetBriefingInputSchema = {
|
|
797
|
-
task:
|
|
872
|
+
task: z15.string().optional().describe(
|
|
798
873
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
799
874
|
),
|
|
800
|
-
files:
|
|
801
|
-
max_tokens:
|
|
875
|
+
files: z15.array(z15.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
876
|
+
max_tokens: z15.number().int().positive().default(8e3).describe(
|
|
802
877
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
803
878
|
),
|
|
804
|
-
max_memories:
|
|
805
|
-
include_project_context:
|
|
806
|
-
include_module_contexts:
|
|
807
|
-
semantic:
|
|
879
|
+
max_memories: z15.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
880
|
+
include_project_context: z15.boolean().default(true),
|
|
881
|
+
include_module_contexts: z15.boolean().default(true),
|
|
882
|
+
semantic: z15.boolean().default(true).describe(
|
|
808
883
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
809
884
|
),
|
|
810
|
-
|
|
885
|
+
include_stale: z15.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
886
|
+
track: z15.boolean().default(true).describe("Increment read_count on returned memories")
|
|
811
887
|
};
|
|
812
888
|
async function getBriefing(input, ctx) {
|
|
813
889
|
const inferred = inferModulesFromPaths2(input.files);
|
|
814
890
|
const memories = [];
|
|
815
891
|
let searchMode = "literal";
|
|
816
|
-
if (
|
|
817
|
-
const
|
|
892
|
+
if (existsSync15(ctx.paths.memoriesDir)) {
|
|
893
|
+
const allLoaded = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
|
|
894
|
+
const allMemories = allLoaded.filter(({ memory }) => {
|
|
895
|
+
const s = memory.frontmatter.status;
|
|
896
|
+
if (s === "rejected" || s === "deprecated") return false;
|
|
897
|
+
if (!input.include_stale && s === "stale") return false;
|
|
898
|
+
return true;
|
|
899
|
+
});
|
|
818
900
|
const usage = await loadUsageIndex7(ctx.paths);
|
|
819
901
|
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
820
902
|
if (input.task && input.semantic) {
|
|
821
903
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
822
904
|
}
|
|
823
905
|
const seen = /* @__PURE__ */ new Map();
|
|
824
|
-
const addOrUpdate = (loaded, reason, score) => {
|
|
906
|
+
const addOrUpdate = (loaded, reason, score, matchQuality) => {
|
|
825
907
|
const fm = loaded.memory.frontmatter;
|
|
826
908
|
const existing = seen.get(fm.id);
|
|
827
909
|
if (existing) {
|
|
@@ -829,6 +911,11 @@ async function getBriefing(input, ctx) {
|
|
|
829
911
|
if (score !== void 0 && (existing.semantic_score ?? 0) < score) {
|
|
830
912
|
existing.semantic_score = score;
|
|
831
913
|
}
|
|
914
|
+
if (matchQuality === "exact" && existing.match_quality !== "exact") {
|
|
915
|
+
existing.match_quality = "exact";
|
|
916
|
+
} else if (matchQuality === "semantic" && existing.match_quality === "partial") {
|
|
917
|
+
existing.match_quality = "semantic";
|
|
918
|
+
}
|
|
832
919
|
return;
|
|
833
920
|
}
|
|
834
921
|
const u = getUsage5(usage, fm.id);
|
|
@@ -842,6 +929,7 @@ async function getBriefing(input, ctx) {
|
|
|
842
929
|
confidence: deriveConfidence4(fm, u),
|
|
843
930
|
read_count: u.read_count,
|
|
844
931
|
reasons: [reason],
|
|
932
|
+
match_quality: matchQuality ?? "partial",
|
|
845
933
|
...score !== void 0 ? { semantic_score: score } : {},
|
|
846
934
|
body: loaded.memory.body,
|
|
847
935
|
file_path: loaded.filePath
|
|
@@ -849,32 +937,33 @@ async function getBriefing(input, ctx) {
|
|
|
849
937
|
};
|
|
850
938
|
if (input.files.length > 0) {
|
|
851
939
|
for (const loaded of allMemories) {
|
|
852
|
-
if (memoryMatchesAnchorPaths2(loaded.memory, input.files)) addOrUpdate(loaded, "anchor");
|
|
940
|
+
if (memoryMatchesAnchorPaths2(loaded.memory, input.files)) addOrUpdate(loaded, "anchor", void 0, "exact");
|
|
853
941
|
}
|
|
854
942
|
for (const loaded of allMemories) {
|
|
855
943
|
const fm = loaded.memory.frontmatter;
|
|
856
|
-
if (fm.module && inferred.includes(fm.module)) addOrUpdate(loaded, "module");
|
|
857
|
-
if (fm.domain && inferred.includes(fm.domain)) addOrUpdate(loaded, "domain");
|
|
858
|
-
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module");
|
|
944
|
+
if (fm.module && inferred.includes(fm.module)) addOrUpdate(loaded, "module", void 0, "partial");
|
|
945
|
+
if (fm.domain && inferred.includes(fm.domain)) addOrUpdate(loaded, "domain", void 0, "partial");
|
|
946
|
+
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module", void 0, "partial");
|
|
859
947
|
}
|
|
860
948
|
}
|
|
861
949
|
if (input.task) {
|
|
862
950
|
const tokens = tokenizeQuery2(input.task);
|
|
863
951
|
for (const loaded of allMemories) {
|
|
864
952
|
if (literalMatchesAllTokens2(loaded.memory, tokens)) {
|
|
865
|
-
addOrUpdate(loaded, "semantic");
|
|
953
|
+
addOrUpdate(loaded, "semantic", void 0, "exact");
|
|
866
954
|
}
|
|
867
955
|
}
|
|
868
956
|
if (semanticHits) {
|
|
869
957
|
const byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
870
958
|
for (const hit of semanticHits) {
|
|
871
959
|
const loaded = byId.get(hit.id);
|
|
872
|
-
if (loaded) addOrUpdate(loaded, "semantic", hit.score);
|
|
960
|
+
if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
|
|
873
961
|
}
|
|
874
962
|
}
|
|
875
963
|
}
|
|
876
964
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
877
|
-
const reasonScore = (m) => (m.
|
|
965
|
+
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
|
|
966
|
+
(m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
878
967
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
879
968
|
const sa = reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
880
969
|
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
@@ -885,10 +974,13 @@ async function getBriefing(input, ctx) {
|
|
|
885
974
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
886
975
|
}
|
|
887
976
|
}
|
|
888
|
-
const projectContext = input.include_project_context &&
|
|
977
|
+
const projectContext = input.include_project_context && existsSync15(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
889
978
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
890
|
-
const memoriesText = memories.map((m) =>
|
|
891
|
-
|
|
979
|
+
const memoriesText = memories.map((m) => {
|
|
980
|
+
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
981
|
+
return `### ${m.id} (${m.scope}/${m.type}, ${m.confidence})${unverified}
|
|
982
|
+
${m.body.trim()}`;
|
|
983
|
+
}).join("\n\n---\n\n");
|
|
892
984
|
const slices = allocateBudget(
|
|
893
985
|
[
|
|
894
986
|
{ key: "project", text: projectContext, weight: 3, mode: "head" },
|
|
@@ -918,12 +1010,24 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
918
1010
|
}
|
|
919
1011
|
}
|
|
920
1012
|
const trimmedMemoriesText = memoriesSlice.text;
|
|
921
|
-
const trimmedMemories =
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1013
|
+
const trimmedMemories = [];
|
|
1014
|
+
if (!memoriesSlice.truncated) {
|
|
1015
|
+
trimmedMemories.push(...memories);
|
|
1016
|
+
} else {
|
|
1017
|
+
let remaining = memoriesSlice.allocatedTokens;
|
|
1018
|
+
for (const m of memories) {
|
|
1019
|
+
const bodyTokens = estimateTokens(m.body);
|
|
1020
|
+
if (remaining <= 0) break;
|
|
1021
|
+
if (bodyTokens <= remaining) {
|
|
1022
|
+
trimmedMemories.push(m);
|
|
1023
|
+
remaining -= bodyTokens;
|
|
1024
|
+
} else if (remaining > 80) {
|
|
1025
|
+
const t = truncateToTokens(m.body, { maxTokens: remaining, mode: "head" });
|
|
1026
|
+
trimmedMemories.push({ ...m, body: t.text });
|
|
1027
|
+
remaining = 0;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
927
1031
|
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
928
1032
|
return {
|
|
929
1033
|
...input.task ? { task: input.task } : {},
|
|
@@ -956,15 +1060,15 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
956
1060
|
}
|
|
957
1061
|
async function loadModuleContexts2(ctx, modules) {
|
|
958
1062
|
if (modules.length === 0) return [];
|
|
959
|
-
if (!
|
|
1063
|
+
if (!existsSync15(ctx.paths.modulesContextDir)) return [];
|
|
960
1064
|
const available = new Set(
|
|
961
1065
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
962
1066
|
);
|
|
963
1067
|
const out = [];
|
|
964
1068
|
for (const m of modules) {
|
|
965
1069
|
if (!available.has(m)) continue;
|
|
966
|
-
const file =
|
|
967
|
-
if (
|
|
1070
|
+
const file = path6.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1071
|
+
if (existsSync15(file)) {
|
|
968
1072
|
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
969
1073
|
}
|
|
970
1074
|
}
|
|
@@ -973,11 +1077,11 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
973
1077
|
|
|
974
1078
|
// src/tools/code-map.ts
|
|
975
1079
|
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
976
|
-
import { z as
|
|
1080
|
+
import { z as z16 } from "zod";
|
|
977
1081
|
var CodeMapInputSchema = {
|
|
978
|
-
file:
|
|
979
|
-
symbol:
|
|
980
|
-
max_files:
|
|
1082
|
+
file: z16.string().optional().describe("Filter to files whose path contains this substring"),
|
|
1083
|
+
symbol: z16.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
1084
|
+
max_files: z16.number().int().positive().default(40).describe("Cap on returned files")
|
|
981
1085
|
};
|
|
982
1086
|
async function codeMapTool(input, ctx) {
|
|
983
1087
|
const map = await loadCodeMap(ctx.paths);
|
|
@@ -1003,12 +1107,12 @@ async function codeMapTool(input, ctx) {
|
|
|
1003
1107
|
}
|
|
1004
1108
|
|
|
1005
1109
|
// src/prompts/bootstrap-project.ts
|
|
1006
|
-
import { z as
|
|
1110
|
+
import { z as z17 } from "zod";
|
|
1007
1111
|
var BootstrapProjectArgsSchema = {
|
|
1008
|
-
module:
|
|
1112
|
+
module: z17.string().optional().describe(
|
|
1009
1113
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1010
1114
|
),
|
|
1011
|
-
focus:
|
|
1115
|
+
focus: z17.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1012
1116
|
};
|
|
1013
1117
|
var ROOT_TEMPLATE = `# Project context
|
|
1014
1118
|
|
|
@@ -1089,9 +1193,69 @@ ${template}\`\`\`
|
|
|
1089
1193
|
};
|
|
1090
1194
|
}
|
|
1091
1195
|
|
|
1196
|
+
// src/prompts/post-task.ts
|
|
1197
|
+
import { z as z18 } from "zod";
|
|
1198
|
+
var PostTaskArgsSchema = {
|
|
1199
|
+
task_summary: z18.string().optional().describe("One sentence describing what you just did"),
|
|
1200
|
+
files_touched: z18.array(z18.string()).optional().describe("Files you created or modified during the task")
|
|
1201
|
+
};
|
|
1202
|
+
function postTaskPrompt(args, ctx) {
|
|
1203
|
+
const taskLine = args.task_summary ? `
|
|
1204
|
+
Task just completed: **${args.task_summary}**` : "";
|
|
1205
|
+
const filesLine = args.files_touched && args.files_touched.length > 0 ? `
|
|
1206
|
+
Files touched: ${args.files_touched.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
1207
|
+
const text = `You have just finished a task. Before closing this session, take 60 seconds to capture what you learned.
|
|
1208
|
+
${taskLine}${filesLine}
|
|
1209
|
+
|
|
1210
|
+
Project root: \`${ctx.paths.root}\`
|
|
1211
|
+
|
|
1212
|
+
## Checklist \u2014 answer each question honestly
|
|
1213
|
+
|
|
1214
|
+
Go through each item. If the answer is yes, call the corresponding tool immediately.
|
|
1215
|
+
|
|
1216
|
+
### 1. Did you try an approach that failed?
|
|
1217
|
+
\u2192 If yes, call **\`mem_tried\`** with:
|
|
1218
|
+
- \`what\`: the approach you tried (e.g. "importing gray-matter with ESM dynamic import")
|
|
1219
|
+
- \`why_failed\`: why it didn't work
|
|
1220
|
+
- \`instead\`: what worked instead
|
|
1221
|
+
- \`scope\`: "team" if others will hit the same issue, "personal" if specific to your setup
|
|
1222
|
+
- \`paths\`: the files where the issue manifested
|
|
1223
|
+
|
|
1224
|
+
### 2. Did you discover a convention that isn't documented?
|
|
1225
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="convention"\` and \`scope="team"\`
|
|
1226
|
+
|
|
1227
|
+
### 3. Did you make an architectural decision?
|
|
1228
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="decision"\` and document the WHY (constraints, tradeoffs), not just the what
|
|
1229
|
+
|
|
1230
|
+
### 4. Did you hit a non-obvious bug or surprising behavior?
|
|
1231
|
+
\u2192 If yes, call **\`mem_save\`** with \`type="gotcha"\` and anchor it to the relevant file paths
|
|
1232
|
+
|
|
1233
|
+
### 5. Did you find that an existing memory is outdated or wrong?
|
|
1234
|
+
\u2192 If yes, call **\`mem_update\`** with the correct information, or **\`mem_reject\`** if it's completely wrong
|
|
1235
|
+
|
|
1236
|
+
## Rules
|
|
1237
|
+
|
|
1238
|
+
- One memory per insight. Don't cram multiple lessons into one body.
|
|
1239
|
+
- Anchor memories to file paths when possible (the \`paths\` field) \u2014 this enables staleness detection.
|
|
1240
|
+
- Prefer \`scope="team"\` for anything a teammate or future agent would benefit from.
|
|
1241
|
+
- Skip sections where you genuinely have nothing to add. Don't fabricate memories.
|
|
1242
|
+
|
|
1243
|
+
When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "Nothing new to save."
|
|
1244
|
+
`;
|
|
1245
|
+
return {
|
|
1246
|
+
description: "Post-task reflection: capture what you learned before closing the session",
|
|
1247
|
+
messages: [
|
|
1248
|
+
{
|
|
1249
|
+
role: "user",
|
|
1250
|
+
content: { type: "text", text }
|
|
1251
|
+
}
|
|
1252
|
+
]
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1092
1256
|
// src/server.ts
|
|
1093
1257
|
var SERVER_NAME = "haive";
|
|
1094
|
-
var SERVER_VERSION = "0.2.
|
|
1258
|
+
var SERVER_VERSION = "0.2.7";
|
|
1095
1259
|
function jsonResult(data) {
|
|
1096
1260
|
return {
|
|
1097
1261
|
content: [
|
|
@@ -1110,7 +1274,7 @@ function createHaiveServer(options = {}) {
|
|
|
1110
1274
|
);
|
|
1111
1275
|
server.tool(
|
|
1112
1276
|
"mem_save",
|
|
1113
|
-
"Save a new memory (
|
|
1277
|
+
"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.",
|
|
1114
1278
|
MemSaveInputSchema,
|
|
1115
1279
|
async (input) => jsonResult(await memSave(input, context))
|
|
1116
1280
|
);
|
|
@@ -1198,12 +1362,24 @@ function createHaiveServer(options = {}) {
|
|
|
1198
1362
|
MemApproveInputSchema,
|
|
1199
1363
|
async (input) => jsonResult(await memApprove(input, context))
|
|
1200
1364
|
);
|
|
1365
|
+
server.tool(
|
|
1366
|
+
"mem_tried",
|
|
1367
|
+
"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.",
|
|
1368
|
+
MemTriedInputSchema,
|
|
1369
|
+
async (input) => jsonResult(await memTried(input, context))
|
|
1370
|
+
);
|
|
1201
1371
|
server.prompt(
|
|
1202
1372
|
"bootstrap_project",
|
|
1203
1373
|
"Instructions for the AI client to analyze the project and save the context.",
|
|
1204
1374
|
BootstrapProjectArgsSchema,
|
|
1205
1375
|
(args) => bootstrapProjectPrompt(args, context)
|
|
1206
1376
|
);
|
|
1377
|
+
server.prompt(
|
|
1378
|
+
"post_task",
|
|
1379
|
+
"Post-task checklist: run this after completing a task to capture failed approaches, new conventions, decisions, and gotchas before closing the session.",
|
|
1380
|
+
PostTaskArgsSchema,
|
|
1381
|
+
(args) => postTaskPrompt(args, context)
|
|
1382
|
+
);
|
|
1207
1383
|
return { server, context };
|
|
1208
1384
|
}
|
|
1209
1385
|
|