@hiveai/mcp 0.2.11 → 0.2.13
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 +248 -51
- package/dist/index.js.map +1 -1
- package/dist/server.js +248 -51
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -119,6 +119,7 @@ async function memList(input, ctx) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// src/tools/mem-save.ts
|
|
122
|
+
import { createHash } from "crypto";
|
|
122
123
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
123
124
|
import { existsSync as existsSync4 } from "fs";
|
|
124
125
|
import path3 from "path";
|
|
@@ -130,7 +131,9 @@ import {
|
|
|
130
131
|
} from "@hiveai/core";
|
|
131
132
|
import { z as z4 } from "zod";
|
|
132
133
|
var MemSaveInputSchema = {
|
|
133
|
-
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).describe(
|
|
134
|
+
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt", "session_recap"]).describe(
|
|
135
|
+
"Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
|
|
136
|
+
),
|
|
134
137
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
135
138
|
body: z4.string().describe("Markdown body of the memory"),
|
|
136
139
|
scope: z4.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope: personal | team | module"),
|
|
@@ -140,14 +143,61 @@ var MemSaveInputSchema = {
|
|
|
140
143
|
author: z4.string().optional().describe("Author handle or email"),
|
|
141
144
|
paths: z4.array(z4.string()).default([]).describe("Anchor paths (file paths this memory references)"),
|
|
142
145
|
symbols: z4.array(z4.string()).default([]).describe("Anchor symbols (function/class names this memory references)"),
|
|
143
|
-
commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)")
|
|
146
|
+
commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)"),
|
|
147
|
+
topic: z4.string().optional().describe(
|
|
148
|
+
"Stable key for this memory. If a memory with the same topic already exists in this scope, it is updated in-place (revision_count++). Use for knowledge that evolves over time."
|
|
149
|
+
)
|
|
144
150
|
};
|
|
151
|
+
function bodyHash(body) {
|
|
152
|
+
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
153
|
+
}
|
|
145
154
|
async function memSave(input, ctx) {
|
|
146
155
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
147
156
|
throw new Error(
|
|
148
157
|
`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`
|
|
149
158
|
);
|
|
150
159
|
}
|
|
160
|
+
const existing = existsSync4(ctx.paths.memoriesDir) ? await loadMemoriesFromDir2(ctx.paths.memoriesDir) : [];
|
|
161
|
+
const incomingHash = bodyHash(input.body);
|
|
162
|
+
const hashDuplicate = existing.find(
|
|
163
|
+
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope === input.scope
|
|
164
|
+
);
|
|
165
|
+
if (hashDuplicate) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Duplicate content detected \u2014 identical body already saved as "${hashDuplicate.memory.frontmatter.id}". Use mem_update to modify it, or change the body to add new information.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (input.topic) {
|
|
171
|
+
const topicMatch = existing.find(
|
|
172
|
+
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
173
|
+
);
|
|
174
|
+
if (topicMatch) {
|
|
175
|
+
const fm = topicMatch.memory.frontmatter;
|
|
176
|
+
const newFrontmatter = {
|
|
177
|
+
...fm,
|
|
178
|
+
body: input.body,
|
|
179
|
+
tags: input.tags.length ? input.tags : fm.tags,
|
|
180
|
+
revision_count: (fm.revision_count ?? 0) + 1,
|
|
181
|
+
anchor: {
|
|
182
|
+
commit: input.commit ?? fm.anchor.commit,
|
|
183
|
+
paths: input.paths.length ? input.paths : fm.anchor.paths,
|
|
184
|
+
symbols: input.symbols.length ? input.symbols : fm.anchor.symbols
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
await writeFile2(
|
|
188
|
+
topicMatch.filePath,
|
|
189
|
+
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
190
|
+
"utf8"
|
|
191
|
+
);
|
|
192
|
+
return {
|
|
193
|
+
id: fm.id,
|
|
194
|
+
scope: fm.scope,
|
|
195
|
+
file_path: topicMatch.filePath,
|
|
196
|
+
action: "updated",
|
|
197
|
+
revision_count: newFrontmatter.revision_count
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
151
201
|
const frontmatter = buildFrontmatter({
|
|
152
202
|
type: input.type,
|
|
153
203
|
slug: input.slug,
|
|
@@ -158,7 +208,8 @@ async function memSave(input, ctx) {
|
|
|
158
208
|
author: input.author,
|
|
159
209
|
paths: input.paths,
|
|
160
210
|
symbols: input.symbols,
|
|
161
|
-
commit: input.commit
|
|
211
|
+
commit: input.commit,
|
|
212
|
+
topic: input.topic
|
|
162
213
|
});
|
|
163
214
|
const file = memoryFilePath(
|
|
164
215
|
ctx.paths,
|
|
@@ -171,15 +222,16 @@ async function memSave(input, ctx) {
|
|
|
171
222
|
throw new Error(`Memory already exists at ${file}`);
|
|
172
223
|
}
|
|
173
224
|
let warning;
|
|
174
|
-
|
|
175
|
-
|
|
225
|
+
let similar_found;
|
|
226
|
+
if (existing.length > 0) {
|
|
176
227
|
const slugTokens = input.slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
177
228
|
const similar = existing.filter(({ memory }) => {
|
|
178
229
|
const id = memory.frontmatter.id.toLowerCase();
|
|
179
230
|
return slugTokens.length >= 2 && slugTokens.filter((t) => id.includes(t)).length >= Math.ceil(slugTokens.length * 0.6);
|
|
180
231
|
});
|
|
181
232
|
if (similar.length > 0) {
|
|
182
|
-
|
|
233
|
+
similar_found = similar.map((m) => m.memory.frontmatter.id);
|
|
234
|
+
warning = `Possible duplicate: similar memories already exist (${similar_found.join(", ")}). Consider updating one of these instead.`;
|
|
183
235
|
}
|
|
184
236
|
}
|
|
185
237
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
@@ -187,7 +239,9 @@ async function memSave(input, ctx) {
|
|
|
187
239
|
id: frontmatter.id,
|
|
188
240
|
scope: frontmatter.scope,
|
|
189
241
|
file_path: file,
|
|
190
|
-
|
|
242
|
+
action: "created",
|
|
243
|
+
...warning ? { warning } : {},
|
|
244
|
+
...similar_found ? { similar_found } : {}
|
|
191
245
|
};
|
|
192
246
|
}
|
|
193
247
|
|
|
@@ -947,10 +1001,120 @@ async function memObserve(input, ctx) {
|
|
|
947
1001
|
return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
|
|
948
1002
|
}
|
|
949
1003
|
|
|
950
|
-
// src/tools/
|
|
951
|
-
import {
|
|
1004
|
+
// src/tools/mem-session-end.ts
|
|
1005
|
+
import { writeFile as writeFile9, mkdir as mkdir5 } from "fs/promises";
|
|
952
1006
|
import { existsSync as existsSync16 } from "fs";
|
|
953
1007
|
import path7 from "path";
|
|
1008
|
+
import {
|
|
1009
|
+
buildFrontmatter as buildFrontmatter4,
|
|
1010
|
+
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
1011
|
+
memoryFilePath as memoryFilePath4,
|
|
1012
|
+
serializeMemory as serializeMemory8
|
|
1013
|
+
} from "@hiveai/core";
|
|
1014
|
+
import { z as z16 } from "zod";
|
|
1015
|
+
var MemSessionEndInputSchema = {
|
|
1016
|
+
goal: z16.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1017
|
+
accomplished: z16.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1018
|
+
discoveries: z16.string().default("").describe(
|
|
1019
|
+
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1020
|
+
),
|
|
1021
|
+
files_touched: z16.array(z16.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1022
|
+
next_steps: z16.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1023
|
+
scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1024
|
+
module: z16.string().optional().describe("Module name (required when scope=module)")
|
|
1025
|
+
};
|
|
1026
|
+
function recapTopic(scope, module) {
|
|
1027
|
+
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
1028
|
+
}
|
|
1029
|
+
function buildBody(input) {
|
|
1030
|
+
const lines = [];
|
|
1031
|
+
lines.push(`## Goal
|
|
1032
|
+
${input.goal}`);
|
|
1033
|
+
lines.push(`
|
|
1034
|
+
## Accomplished
|
|
1035
|
+
${input.accomplished}`);
|
|
1036
|
+
if (input.discoveries.trim()) {
|
|
1037
|
+
lines.push(`
|
|
1038
|
+
## Discoveries & surprises
|
|
1039
|
+
${input.discoveries}`);
|
|
1040
|
+
}
|
|
1041
|
+
if (input.files_touched.length > 0) {
|
|
1042
|
+
lines.push(`
|
|
1043
|
+
## Files touched
|
|
1044
|
+
${input.files_touched.map((f) => `- \`${f}\``).join("\n")}`);
|
|
1045
|
+
}
|
|
1046
|
+
if (input.next_steps.trim()) {
|
|
1047
|
+
lines.push(`
|
|
1048
|
+
## Next steps
|
|
1049
|
+
${input.next_steps}`);
|
|
1050
|
+
}
|
|
1051
|
+
return lines.join("\n");
|
|
1052
|
+
}
|
|
1053
|
+
async function memSessionEnd(input, ctx) {
|
|
1054
|
+
if (!existsSync16(ctx.paths.haiveDir)) {
|
|
1055
|
+
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
1056
|
+
}
|
|
1057
|
+
const body = buildBody(input);
|
|
1058
|
+
const topic = recapTopic(input.scope, input.module);
|
|
1059
|
+
const existing = existsSync16(ctx.paths.memoriesDir) ? await loadMemoriesFromDir12(ctx.paths.memoriesDir) : [];
|
|
1060
|
+
const topicMatch = existing.find(
|
|
1061
|
+
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1062
|
+
);
|
|
1063
|
+
if (topicMatch) {
|
|
1064
|
+
const fm = topicMatch.memory.frontmatter;
|
|
1065
|
+
const revisionCount = (fm.revision_count ?? 0) + 1;
|
|
1066
|
+
const newFrontmatter = {
|
|
1067
|
+
...fm,
|
|
1068
|
+
revision_count: revisionCount,
|
|
1069
|
+
anchor: {
|
|
1070
|
+
...fm.anchor,
|
|
1071
|
+
paths: input.files_touched.length ? input.files_touched : fm.anchor.paths
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
await writeFile9(
|
|
1075
|
+
topicMatch.filePath,
|
|
1076
|
+
serializeMemory8({ frontmatter: newFrontmatter, body }),
|
|
1077
|
+
"utf8"
|
|
1078
|
+
);
|
|
1079
|
+
return {
|
|
1080
|
+
id: fm.id,
|
|
1081
|
+
scope: fm.scope,
|
|
1082
|
+
file_path: topicMatch.filePath,
|
|
1083
|
+
action: "updated",
|
|
1084
|
+
revision_count: revisionCount
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
const frontmatter = buildFrontmatter4({
|
|
1088
|
+
type: "session_recap",
|
|
1089
|
+
slug: "recap",
|
|
1090
|
+
scope: input.scope,
|
|
1091
|
+
module: input.module,
|
|
1092
|
+
tags: ["session", "recap"],
|
|
1093
|
+
paths: input.files_touched,
|
|
1094
|
+
topic,
|
|
1095
|
+
status: "validated"
|
|
1096
|
+
});
|
|
1097
|
+
const file = memoryFilePath4(
|
|
1098
|
+
ctx.paths,
|
|
1099
|
+
frontmatter.scope,
|
|
1100
|
+
frontmatter.id,
|
|
1101
|
+
frontmatter.module
|
|
1102
|
+
);
|
|
1103
|
+
await mkdir5(path7.dirname(file), { recursive: true });
|
|
1104
|
+
await writeFile9(file, serializeMemory8({ frontmatter, body }), "utf8");
|
|
1105
|
+
return {
|
|
1106
|
+
id: frontmatter.id,
|
|
1107
|
+
scope: frontmatter.scope,
|
|
1108
|
+
file_path: file,
|
|
1109
|
+
action: "created",
|
|
1110
|
+
revision_count: 0
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/tools/get-briefing.ts
|
|
1115
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
1116
|
+
import { existsSync as existsSync17 } from "fs";
|
|
1117
|
+
import path8 from "path";
|
|
954
1118
|
import {
|
|
955
1119
|
allocateBudget,
|
|
956
1120
|
deriveConfidence as deriveConfidence4,
|
|
@@ -959,31 +1123,31 @@ import {
|
|
|
959
1123
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
960
1124
|
isDecaying,
|
|
961
1125
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
962
|
-
loadMemoriesFromDir as
|
|
1126
|
+
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
963
1127
|
loadUsageIndex as loadUsageIndex7,
|
|
964
1128
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
965
1129
|
tokenizeQuery as tokenizeQuery2,
|
|
966
1130
|
trackReads as trackReads3,
|
|
967
1131
|
truncateToTokens
|
|
968
1132
|
} from "@hiveai/core";
|
|
969
|
-
import { z as
|
|
1133
|
+
import { z as z17 } from "zod";
|
|
970
1134
|
var GetBriefingInputSchema = {
|
|
971
|
-
task:
|
|
1135
|
+
task: z17.string().optional().describe(
|
|
972
1136
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
973
1137
|
),
|
|
974
|
-
files:
|
|
975
|
-
max_tokens:
|
|
1138
|
+
files: z17.array(z17.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
1139
|
+
max_tokens: z17.number().int().positive().default(8e3).describe(
|
|
976
1140
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
977
1141
|
),
|
|
978
|
-
max_memories:
|
|
979
|
-
include_project_context:
|
|
980
|
-
include_module_contexts:
|
|
981
|
-
semantic:
|
|
1142
|
+
max_memories: z17.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
1143
|
+
include_project_context: z17.boolean().default(true),
|
|
1144
|
+
include_module_contexts: z17.boolean().default(true),
|
|
1145
|
+
semantic: z17.boolean().default(true).describe(
|
|
982
1146
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
983
1147
|
),
|
|
984
|
-
include_stale:
|
|
985
|
-
track:
|
|
986
|
-
format:
|
|
1148
|
+
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1149
|
+
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1150
|
+
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
987
1151
|
"Output format: 'full' returns complete memory bodies; 'compact' returns id + 1-line summary only (call mem_get for details)."
|
|
988
1152
|
)
|
|
989
1153
|
};
|
|
@@ -993,12 +1157,27 @@ async function getBriefing(input, ctx) {
|
|
|
993
1157
|
let searchMode = "literal";
|
|
994
1158
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
995
1159
|
let byId = /* @__PURE__ */ new Map();
|
|
996
|
-
|
|
997
|
-
|
|
1160
|
+
let lastSession;
|
|
1161
|
+
if (existsSync17(ctx.paths.memoriesDir)) {
|
|
1162
|
+
const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1163
|
+
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
1164
|
+
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
1165
|
+
);
|
|
1166
|
+
if (recaps.length > 0) {
|
|
1167
|
+
const r = recaps[0];
|
|
1168
|
+
const fm = r.memory.frontmatter;
|
|
1169
|
+
lastSession = {
|
|
1170
|
+
id: fm.id,
|
|
1171
|
+
scope: fm.scope,
|
|
1172
|
+
revision_count: fm.revision_count ?? 0,
|
|
1173
|
+
body: r.memory.body
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
998
1176
|
const allMemories = allLoaded.filter(({ memory }) => {
|
|
999
1177
|
const s = memory.frontmatter.status;
|
|
1000
1178
|
if (s === "rejected" || s === "deprecated") return false;
|
|
1001
1179
|
if (!input.include_stale && s === "stale") return false;
|
|
1180
|
+
if (memory.frontmatter.type === "session_recap") return false;
|
|
1002
1181
|
return true;
|
|
1003
1182
|
});
|
|
1004
1183
|
usage = await loadUsageIndex7(ctx.paths);
|
|
@@ -1088,7 +1267,7 @@ async function getBriefing(input, ctx) {
|
|
|
1088
1267
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1089
1268
|
}
|
|
1090
1269
|
}
|
|
1091
|
-
const projectContext = input.include_project_context &&
|
|
1270
|
+
const projectContext = input.include_project_context && existsSync17(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
1092
1271
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1093
1272
|
const memoriesText = memories.map((m) => {
|
|
1094
1273
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
@@ -1154,6 +1333,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1154
1333
|
...input.task ? { task: input.task } : {},
|
|
1155
1334
|
search_mode: searchMode,
|
|
1156
1335
|
inferred_modules: inferred,
|
|
1336
|
+
...lastSession ? { last_session: lastSession } : {},
|
|
1157
1337
|
project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
|
|
1158
1338
|
module_contexts: trimmedModules,
|
|
1159
1339
|
memories: outputMemories,
|
|
@@ -1189,15 +1369,15 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
1189
1369
|
}
|
|
1190
1370
|
async function loadModuleContexts2(ctx, modules) {
|
|
1191
1371
|
if (modules.length === 0) return [];
|
|
1192
|
-
if (!
|
|
1372
|
+
if (!existsSync17(ctx.paths.modulesContextDir)) return [];
|
|
1193
1373
|
const available = new Set(
|
|
1194
1374
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
1195
1375
|
);
|
|
1196
1376
|
const out = [];
|
|
1197
1377
|
for (const m of modules) {
|
|
1198
1378
|
if (!available.has(m)) continue;
|
|
1199
|
-
const file =
|
|
1200
|
-
if (
|
|
1379
|
+
const file = path8.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1380
|
+
if (existsSync17(file)) {
|
|
1201
1381
|
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
1202
1382
|
}
|
|
1203
1383
|
}
|
|
@@ -1206,11 +1386,11 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
1206
1386
|
|
|
1207
1387
|
// src/tools/code-map.ts
|
|
1208
1388
|
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
1209
|
-
import { z as
|
|
1389
|
+
import { z as z18 } from "zod";
|
|
1210
1390
|
var CodeMapInputSchema = {
|
|
1211
|
-
file:
|
|
1212
|
-
symbol:
|
|
1213
|
-
max_files:
|
|
1391
|
+
file: z18.string().optional().describe("Filter to files whose path contains this substring"),
|
|
1392
|
+
symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
1393
|
+
max_files: z18.number().int().positive().default(40).describe("Cap on returned files")
|
|
1214
1394
|
};
|
|
1215
1395
|
async function codeMapTool(input, ctx) {
|
|
1216
1396
|
const map = await loadCodeMap(ctx.paths);
|
|
@@ -1236,18 +1416,18 @@ async function codeMapTool(input, ctx) {
|
|
|
1236
1416
|
}
|
|
1237
1417
|
|
|
1238
1418
|
// src/tools/mem-diff.ts
|
|
1239
|
-
import { existsSync as
|
|
1240
|
-
import { loadMemoriesFromDir as
|
|
1241
|
-
import { z as
|
|
1419
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1420
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
|
|
1421
|
+
import { z as z19 } from "zod";
|
|
1242
1422
|
var MemDiffInputSchema = {
|
|
1243
|
-
id_a:
|
|
1244
|
-
id_b:
|
|
1423
|
+
id_a: z19.string().min(1).describe("First memory id"),
|
|
1424
|
+
id_b: z19.string().min(1).describe("Second memory id")
|
|
1245
1425
|
};
|
|
1246
1426
|
async function memDiff(input, ctx) {
|
|
1247
|
-
if (!
|
|
1427
|
+
if (!existsSync18(ctx.paths.memoriesDir)) {
|
|
1248
1428
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
1249
1429
|
}
|
|
1250
|
-
const all = await
|
|
1430
|
+
const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
1251
1431
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
1252
1432
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
1253
1433
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -1281,12 +1461,12 @@ async function memDiff(input, ctx) {
|
|
|
1281
1461
|
}
|
|
1282
1462
|
|
|
1283
1463
|
// src/prompts/bootstrap-project.ts
|
|
1284
|
-
import { z as
|
|
1464
|
+
import { z as z20 } from "zod";
|
|
1285
1465
|
var BootstrapProjectArgsSchema = {
|
|
1286
|
-
module:
|
|
1466
|
+
module: z20.string().optional().describe(
|
|
1287
1467
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1288
1468
|
),
|
|
1289
|
-
focus:
|
|
1469
|
+
focus: z20.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1290
1470
|
};
|
|
1291
1471
|
var ROOT_TEMPLATE = `# Project context
|
|
1292
1472
|
|
|
@@ -1368,10 +1548,10 @@ ${template}\`\`\`
|
|
|
1368
1548
|
}
|
|
1369
1549
|
|
|
1370
1550
|
// src/prompts/post-task.ts
|
|
1371
|
-
import { z as
|
|
1551
|
+
import { z as z21 } from "zod";
|
|
1372
1552
|
var PostTaskArgsSchema = {
|
|
1373
|
-
task_summary:
|
|
1374
|
-
files_touched:
|
|
1553
|
+
task_summary: z21.string().optional().describe("One sentence describing what you just did"),
|
|
1554
|
+
files_touched: z21.array(z21.string()).optional().describe("Files you created or modified during the task")
|
|
1375
1555
|
};
|
|
1376
1556
|
function postTaskPrompt(args, ctx) {
|
|
1377
1557
|
const taskLine = args.task_summary ? `
|
|
@@ -1428,7 +1608,18 @@ Examples of things to look for:
|
|
|
1428
1608
|
- Skip sections where you genuinely have nothing to add. Don't fabricate memories.
|
|
1429
1609
|
- **Question 0 is not optional** \u2014 always scan your exploration history for code-level discoveries.
|
|
1430
1610
|
|
|
1431
|
-
|
|
1611
|
+
### 6. Close the session \u2014 always
|
|
1612
|
+
Call **\`mem_session_end\`** with:
|
|
1613
|
+
- \`goal\`: what you set out to do
|
|
1614
|
+
- \`accomplished\`: what was actually done (bullet list)
|
|
1615
|
+
- \`discoveries\`: anything surprising or broken found during this session (leave empty if none)
|
|
1616
|
+
- \`files_touched\`: the key files you read or modified
|
|
1617
|
+
- \`next_steps\`: what remains for the next session or a teammate
|
|
1618
|
+
- \`scope\`: "team" if this task affects the whole team, "personal" otherwise
|
|
1619
|
+
|
|
1620
|
+
This creates/updates a single rolling recap that **get_briefing automatically surfaces** at the start of every subsequent session \u2014 no token waste re-explaining what happened.
|
|
1621
|
+
|
|
1622
|
+
When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved."
|
|
1432
1623
|
`;
|
|
1433
1624
|
return {
|
|
1434
1625
|
description: "Post-task reflection: capture what you learned before closing the session",
|
|
@@ -1442,12 +1633,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "N
|
|
|
1442
1633
|
}
|
|
1443
1634
|
|
|
1444
1635
|
// src/prompts/import-docs.ts
|
|
1445
|
-
import { z as
|
|
1636
|
+
import { z as z22 } from "zod";
|
|
1446
1637
|
var ImportDocsArgsSchema = {
|
|
1447
|
-
content:
|
|
1448
|
-
source:
|
|
1449
|
-
scope:
|
|
1450
|
-
dry_run:
|
|
1638
|
+
content: z22.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
1639
|
+
source: z22.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
1640
|
+
scope: z22.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
1641
|
+
dry_run: z22.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
1451
1642
|
};
|
|
1452
1643
|
function importDocsPrompt(args, ctx) {
|
|
1453
1644
|
const sourceLine = args.source ? `
|
|
@@ -1512,7 +1703,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
1512
1703
|
|
|
1513
1704
|
// src/server.ts
|
|
1514
1705
|
var SERVER_NAME = "haive";
|
|
1515
|
-
var SERVER_VERSION = "0.2.
|
|
1706
|
+
var SERVER_VERSION = "0.2.13";
|
|
1516
1707
|
function jsonResult(data) {
|
|
1517
1708
|
return {
|
|
1518
1709
|
content: [
|
|
@@ -1637,6 +1828,12 @@ function createHaiveServer(options = {}) {
|
|
|
1637
1828
|
MemObserveInputSchema,
|
|
1638
1829
|
async (input) => jsonResult(await memObserve(input, context))
|
|
1639
1830
|
);
|
|
1831
|
+
server.tool(
|
|
1832
|
+
"mem_session_end",
|
|
1833
|
+
"Save a structured end-of-session recap (goal / accomplished / discoveries / next steps). Uses topic-upsert: one recap per scope is kept and updated in-place so the next session always has fresh context. Call this before closing every significant session. get_briefing automatically surfaces the latest recap at the top of the next session's briefing.",
|
|
1834
|
+
MemSessionEndInputSchema,
|
|
1835
|
+
async (input) => jsonResult(await memSessionEnd(input, context))
|
|
1836
|
+
);
|
|
1640
1837
|
server.prompt(
|
|
1641
1838
|
"bootstrap_project",
|
|
1642
1839
|
"Instructions for the AI client to analyze the project and save the context.",
|