@hiveai/mcp 0.2.11 → 0.2.12
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/index.js
CHANGED
|
@@ -124,6 +124,7 @@ async function memList(input, ctx) {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// src/tools/mem-save.ts
|
|
127
|
+
import { createHash } from "crypto";
|
|
127
128
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
128
129
|
import { existsSync as existsSync4 } from "fs";
|
|
129
130
|
import path3 from "path";
|
|
@@ -135,7 +136,9 @@ import {
|
|
|
135
136
|
} from "@hiveai/core";
|
|
136
137
|
import { z as z4 } from "zod";
|
|
137
138
|
var MemSaveInputSchema = {
|
|
138
|
-
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt"]).describe(
|
|
139
|
+
type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt", "session_recap"]).describe(
|
|
140
|
+
"Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
|
|
141
|
+
),
|
|
139
142
|
slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
|
|
140
143
|
body: z4.string().describe("Markdown body of the memory"),
|
|
141
144
|
scope: z4.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope: personal | team | module"),
|
|
@@ -145,14 +148,61 @@ var MemSaveInputSchema = {
|
|
|
145
148
|
author: z4.string().optional().describe("Author handle or email"),
|
|
146
149
|
paths: z4.array(z4.string()).default([]).describe("Anchor paths (file paths this memory references)"),
|
|
147
150
|
symbols: z4.array(z4.string()).default([]).describe("Anchor symbols (function/class names this memory references)"),
|
|
148
|
-
commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)")
|
|
151
|
+
commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)"),
|
|
152
|
+
topic: z4.string().optional().describe(
|
|
153
|
+
"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."
|
|
154
|
+
)
|
|
149
155
|
};
|
|
156
|
+
function bodyHash(body) {
|
|
157
|
+
return createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
158
|
+
}
|
|
150
159
|
async function memSave(input, ctx) {
|
|
151
160
|
if (!existsSync4(ctx.paths.haiveDir)) {
|
|
152
161
|
throw new Error(
|
|
153
162
|
`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`
|
|
154
163
|
);
|
|
155
164
|
}
|
|
165
|
+
const existing = existsSync4(ctx.paths.memoriesDir) ? await loadMemoriesFromDir2(ctx.paths.memoriesDir) : [];
|
|
166
|
+
const incomingHash = bodyHash(input.body);
|
|
167
|
+
const hashDuplicate = existing.find(
|
|
168
|
+
({ memory }) => bodyHash(memory.body) === incomingHash && memory.frontmatter.scope === input.scope
|
|
169
|
+
);
|
|
170
|
+
if (hashDuplicate) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`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.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (input.topic) {
|
|
176
|
+
const topicMatch = existing.find(
|
|
177
|
+
({ memory }) => memory.frontmatter.topic === input.topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
178
|
+
);
|
|
179
|
+
if (topicMatch) {
|
|
180
|
+
const fm = topicMatch.memory.frontmatter;
|
|
181
|
+
const newFrontmatter = {
|
|
182
|
+
...fm,
|
|
183
|
+
body: input.body,
|
|
184
|
+
tags: input.tags.length ? input.tags : fm.tags,
|
|
185
|
+
revision_count: (fm.revision_count ?? 0) + 1,
|
|
186
|
+
anchor: {
|
|
187
|
+
commit: input.commit ?? fm.anchor.commit,
|
|
188
|
+
paths: input.paths.length ? input.paths : fm.anchor.paths,
|
|
189
|
+
symbols: input.symbols.length ? input.symbols : fm.anchor.symbols
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
await writeFile2(
|
|
193
|
+
topicMatch.filePath,
|
|
194
|
+
serializeMemory({ frontmatter: newFrontmatter, body: input.body }),
|
|
195
|
+
"utf8"
|
|
196
|
+
);
|
|
197
|
+
return {
|
|
198
|
+
id: fm.id,
|
|
199
|
+
scope: fm.scope,
|
|
200
|
+
file_path: topicMatch.filePath,
|
|
201
|
+
action: "updated",
|
|
202
|
+
revision_count: newFrontmatter.revision_count
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
156
206
|
const frontmatter = buildFrontmatter({
|
|
157
207
|
type: input.type,
|
|
158
208
|
slug: input.slug,
|
|
@@ -163,7 +213,8 @@ async function memSave(input, ctx) {
|
|
|
163
213
|
author: input.author,
|
|
164
214
|
paths: input.paths,
|
|
165
215
|
symbols: input.symbols,
|
|
166
|
-
commit: input.commit
|
|
216
|
+
commit: input.commit,
|
|
217
|
+
topic: input.topic
|
|
167
218
|
});
|
|
168
219
|
const file = memoryFilePath(
|
|
169
220
|
ctx.paths,
|
|
@@ -176,15 +227,16 @@ async function memSave(input, ctx) {
|
|
|
176
227
|
throw new Error(`Memory already exists at ${file}`);
|
|
177
228
|
}
|
|
178
229
|
let warning;
|
|
179
|
-
|
|
180
|
-
|
|
230
|
+
let similar_found;
|
|
231
|
+
if (existing.length > 0) {
|
|
181
232
|
const slugTokens = input.slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
182
233
|
const similar = existing.filter(({ memory }) => {
|
|
183
234
|
const id = memory.frontmatter.id.toLowerCase();
|
|
184
235
|
return slugTokens.length >= 2 && slugTokens.filter((t) => id.includes(t)).length >= Math.ceil(slugTokens.length * 0.6);
|
|
185
236
|
});
|
|
186
237
|
if (similar.length > 0) {
|
|
187
|
-
|
|
238
|
+
similar_found = similar.map((m) => m.memory.frontmatter.id);
|
|
239
|
+
warning = `Possible duplicate: similar memories already exist (${similar_found.join(", ")}). Consider updating one of these instead.`;
|
|
188
240
|
}
|
|
189
241
|
}
|
|
190
242
|
await writeFile2(file, serializeMemory({ frontmatter, body: input.body }), "utf8");
|
|
@@ -192,7 +244,9 @@ async function memSave(input, ctx) {
|
|
|
192
244
|
id: frontmatter.id,
|
|
193
245
|
scope: frontmatter.scope,
|
|
194
246
|
file_path: file,
|
|
195
|
-
|
|
247
|
+
action: "created",
|
|
248
|
+
...warning ? { warning } : {},
|
|
249
|
+
...similar_found ? { similar_found } : {}
|
|
196
250
|
};
|
|
197
251
|
}
|
|
198
252
|
|
|
@@ -952,10 +1006,120 @@ async function memObserve(input, ctx) {
|
|
|
952
1006
|
return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
|
|
953
1007
|
}
|
|
954
1008
|
|
|
955
|
-
// src/tools/
|
|
956
|
-
import {
|
|
1009
|
+
// src/tools/mem-session-end.ts
|
|
1010
|
+
import { writeFile as writeFile9, mkdir as mkdir5 } from "fs/promises";
|
|
957
1011
|
import { existsSync as existsSync16 } from "fs";
|
|
958
1012
|
import path7 from "path";
|
|
1013
|
+
import {
|
|
1014
|
+
buildFrontmatter as buildFrontmatter4,
|
|
1015
|
+
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
1016
|
+
memoryFilePath as memoryFilePath4,
|
|
1017
|
+
serializeMemory as serializeMemory8
|
|
1018
|
+
} from "@hiveai/core";
|
|
1019
|
+
import { z as z16 } from "zod";
|
|
1020
|
+
var MemSessionEndInputSchema = {
|
|
1021
|
+
goal: z16.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1022
|
+
accomplished: z16.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1023
|
+
discoveries: z16.string().default("").describe(
|
|
1024
|
+
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1025
|
+
),
|
|
1026
|
+
files_touched: z16.array(z16.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1027
|
+
next_steps: z16.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1028
|
+
scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1029
|
+
module: z16.string().optional().describe("Module name (required when scope=module)")
|
|
1030
|
+
};
|
|
1031
|
+
function recapTopic(scope, module) {
|
|
1032
|
+
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
1033
|
+
}
|
|
1034
|
+
function buildBody(input) {
|
|
1035
|
+
const lines = [];
|
|
1036
|
+
lines.push(`## Goal
|
|
1037
|
+
${input.goal}`);
|
|
1038
|
+
lines.push(`
|
|
1039
|
+
## Accomplished
|
|
1040
|
+
${input.accomplished}`);
|
|
1041
|
+
if (input.discoveries.trim()) {
|
|
1042
|
+
lines.push(`
|
|
1043
|
+
## Discoveries & surprises
|
|
1044
|
+
${input.discoveries}`);
|
|
1045
|
+
}
|
|
1046
|
+
if (input.files_touched.length > 0) {
|
|
1047
|
+
lines.push(`
|
|
1048
|
+
## Files touched
|
|
1049
|
+
${input.files_touched.map((f) => `- \`${f}\``).join("\n")}`);
|
|
1050
|
+
}
|
|
1051
|
+
if (input.next_steps.trim()) {
|
|
1052
|
+
lines.push(`
|
|
1053
|
+
## Next steps
|
|
1054
|
+
${input.next_steps}`);
|
|
1055
|
+
}
|
|
1056
|
+
return lines.join("\n");
|
|
1057
|
+
}
|
|
1058
|
+
async function memSessionEnd(input, ctx) {
|
|
1059
|
+
if (!existsSync16(ctx.paths.haiveDir)) {
|
|
1060
|
+
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
1061
|
+
}
|
|
1062
|
+
const body = buildBody(input);
|
|
1063
|
+
const topic = recapTopic(input.scope, input.module);
|
|
1064
|
+
const existing = existsSync16(ctx.paths.memoriesDir) ? await loadMemoriesFromDir12(ctx.paths.memoriesDir) : [];
|
|
1065
|
+
const topicMatch = existing.find(
|
|
1066
|
+
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1067
|
+
);
|
|
1068
|
+
if (topicMatch) {
|
|
1069
|
+
const fm = topicMatch.memory.frontmatter;
|
|
1070
|
+
const revisionCount = (fm.revision_count ?? 0) + 1;
|
|
1071
|
+
const newFrontmatter = {
|
|
1072
|
+
...fm,
|
|
1073
|
+
revision_count: revisionCount,
|
|
1074
|
+
anchor: {
|
|
1075
|
+
...fm.anchor,
|
|
1076
|
+
paths: input.files_touched.length ? input.files_touched : fm.anchor.paths
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
await writeFile9(
|
|
1080
|
+
topicMatch.filePath,
|
|
1081
|
+
serializeMemory8({ frontmatter: newFrontmatter, body }),
|
|
1082
|
+
"utf8"
|
|
1083
|
+
);
|
|
1084
|
+
return {
|
|
1085
|
+
id: fm.id,
|
|
1086
|
+
scope: fm.scope,
|
|
1087
|
+
file_path: topicMatch.filePath,
|
|
1088
|
+
action: "updated",
|
|
1089
|
+
revision_count: revisionCount
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
const frontmatter = buildFrontmatter4({
|
|
1093
|
+
type: "session_recap",
|
|
1094
|
+
slug: "session-recap",
|
|
1095
|
+
scope: input.scope,
|
|
1096
|
+
module: input.module,
|
|
1097
|
+
tags: ["session", "recap"],
|
|
1098
|
+
paths: input.files_touched,
|
|
1099
|
+
topic,
|
|
1100
|
+
status: "validated"
|
|
1101
|
+
});
|
|
1102
|
+
const file = memoryFilePath4(
|
|
1103
|
+
ctx.paths,
|
|
1104
|
+
frontmatter.scope,
|
|
1105
|
+
frontmatter.id,
|
|
1106
|
+
frontmatter.module
|
|
1107
|
+
);
|
|
1108
|
+
await mkdir5(path7.dirname(file), { recursive: true });
|
|
1109
|
+
await writeFile9(file, serializeMemory8({ frontmatter, body }), "utf8");
|
|
1110
|
+
return {
|
|
1111
|
+
id: frontmatter.id,
|
|
1112
|
+
scope: frontmatter.scope,
|
|
1113
|
+
file_path: file,
|
|
1114
|
+
action: "created",
|
|
1115
|
+
revision_count: 0
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// src/tools/get-briefing.ts
|
|
1120
|
+
import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
|
|
1121
|
+
import { existsSync as existsSync17 } from "fs";
|
|
1122
|
+
import path8 from "path";
|
|
959
1123
|
import {
|
|
960
1124
|
allocateBudget,
|
|
961
1125
|
deriveConfidence as deriveConfidence4,
|
|
@@ -964,31 +1128,31 @@ import {
|
|
|
964
1128
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
965
1129
|
isDecaying,
|
|
966
1130
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
967
|
-
loadMemoriesFromDir as
|
|
1131
|
+
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
968
1132
|
loadUsageIndex as loadUsageIndex7,
|
|
969
1133
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
970
1134
|
tokenizeQuery as tokenizeQuery2,
|
|
971
1135
|
trackReads as trackReads3,
|
|
972
1136
|
truncateToTokens
|
|
973
1137
|
} from "@hiveai/core";
|
|
974
|
-
import { z as
|
|
1138
|
+
import { z as z17 } from "zod";
|
|
975
1139
|
var GetBriefingInputSchema = {
|
|
976
|
-
task:
|
|
1140
|
+
task: z17.string().optional().describe(
|
|
977
1141
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
978
1142
|
),
|
|
979
|
-
files:
|
|
980
|
-
max_tokens:
|
|
1143
|
+
files: z17.array(z17.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
1144
|
+
max_tokens: z17.number().int().positive().default(8e3).describe(
|
|
981
1145
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
982
1146
|
),
|
|
983
|
-
max_memories:
|
|
984
|
-
include_project_context:
|
|
985
|
-
include_module_contexts:
|
|
986
|
-
semantic:
|
|
1147
|
+
max_memories: z17.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
1148
|
+
include_project_context: z17.boolean().default(true),
|
|
1149
|
+
include_module_contexts: z17.boolean().default(true),
|
|
1150
|
+
semantic: z17.boolean().default(true).describe(
|
|
987
1151
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
988
1152
|
),
|
|
989
|
-
include_stale:
|
|
990
|
-
track:
|
|
991
|
-
format:
|
|
1153
|
+
include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1154
|
+
track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1155
|
+
format: z17.enum(["full", "compact"]).default("full").describe(
|
|
992
1156
|
"Output format: 'full' returns complete memory bodies; 'compact' returns id + 1-line summary only (call mem_get for details)."
|
|
993
1157
|
)
|
|
994
1158
|
};
|
|
@@ -998,12 +1162,27 @@ async function getBriefing(input, ctx) {
|
|
|
998
1162
|
let searchMode = "literal";
|
|
999
1163
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
1000
1164
|
let byId = /* @__PURE__ */ new Map();
|
|
1001
|
-
|
|
1002
|
-
|
|
1165
|
+
let lastSession;
|
|
1166
|
+
if (existsSync17(ctx.paths.memoriesDir)) {
|
|
1167
|
+
const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
|
|
1168
|
+
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
1169
|
+
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
1170
|
+
);
|
|
1171
|
+
if (recaps.length > 0) {
|
|
1172
|
+
const r = recaps[0];
|
|
1173
|
+
const fm = r.memory.frontmatter;
|
|
1174
|
+
lastSession = {
|
|
1175
|
+
id: fm.id,
|
|
1176
|
+
scope: fm.scope,
|
|
1177
|
+
revision_count: fm.revision_count ?? 0,
|
|
1178
|
+
body: r.memory.body
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1003
1181
|
const allMemories = allLoaded.filter(({ memory }) => {
|
|
1004
1182
|
const s = memory.frontmatter.status;
|
|
1005
1183
|
if (s === "rejected" || s === "deprecated") return false;
|
|
1006
1184
|
if (!input.include_stale && s === "stale") return false;
|
|
1185
|
+
if (memory.frontmatter.type === "session_recap") return false;
|
|
1007
1186
|
return true;
|
|
1008
1187
|
});
|
|
1009
1188
|
usage = await loadUsageIndex7(ctx.paths);
|
|
@@ -1093,7 +1272,7 @@ async function getBriefing(input, ctx) {
|
|
|
1093
1272
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1094
1273
|
}
|
|
1095
1274
|
}
|
|
1096
|
-
const projectContext = input.include_project_context &&
|
|
1275
|
+
const projectContext = input.include_project_context && existsSync17(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
1097
1276
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1098
1277
|
const memoriesText = memories.map((m) => {
|
|
1099
1278
|
const unverified = m.status === "proposed" ? " [UNVERIFIED \u2014 not yet validated]" : "";
|
|
@@ -1159,6 +1338,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1159
1338
|
...input.task ? { task: input.task } : {},
|
|
1160
1339
|
search_mode: searchMode,
|
|
1161
1340
|
inferred_modules: inferred,
|
|
1341
|
+
...lastSession ? { last_session: lastSession } : {},
|
|
1162
1342
|
project_context: projectContext ? { content: projectSlice.text, truncated: projectSlice.truncated } : null,
|
|
1163
1343
|
module_contexts: trimmedModules,
|
|
1164
1344
|
memories: outputMemories,
|
|
@@ -1194,15 +1374,15 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
1194
1374
|
}
|
|
1195
1375
|
async function loadModuleContexts2(ctx, modules) {
|
|
1196
1376
|
if (modules.length === 0) return [];
|
|
1197
|
-
if (!
|
|
1377
|
+
if (!existsSync17(ctx.paths.modulesContextDir)) return [];
|
|
1198
1378
|
const available = new Set(
|
|
1199
1379
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
1200
1380
|
);
|
|
1201
1381
|
const out = [];
|
|
1202
1382
|
for (const m of modules) {
|
|
1203
1383
|
if (!available.has(m)) continue;
|
|
1204
|
-
const file =
|
|
1205
|
-
if (
|
|
1384
|
+
const file = path8.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1385
|
+
if (existsSync17(file)) {
|
|
1206
1386
|
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
1207
1387
|
}
|
|
1208
1388
|
}
|
|
@@ -1211,11 +1391,11 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
1211
1391
|
|
|
1212
1392
|
// src/tools/code-map.ts
|
|
1213
1393
|
import { loadCodeMap, queryCodeMap } from "@hiveai/core";
|
|
1214
|
-
import { z as
|
|
1394
|
+
import { z as z18 } from "zod";
|
|
1215
1395
|
var CodeMapInputSchema = {
|
|
1216
|
-
file:
|
|
1217
|
-
symbol:
|
|
1218
|
-
max_files:
|
|
1396
|
+
file: z18.string().optional().describe("Filter to files whose path contains this substring"),
|
|
1397
|
+
symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
1398
|
+
max_files: z18.number().int().positive().default(40).describe("Cap on returned files")
|
|
1219
1399
|
};
|
|
1220
1400
|
async function codeMapTool(input, ctx) {
|
|
1221
1401
|
const map = await loadCodeMap(ctx.paths);
|
|
@@ -1241,18 +1421,18 @@ async function codeMapTool(input, ctx) {
|
|
|
1241
1421
|
}
|
|
1242
1422
|
|
|
1243
1423
|
// src/tools/mem-diff.ts
|
|
1244
|
-
import { existsSync as
|
|
1245
|
-
import { loadMemoriesFromDir as
|
|
1246
|
-
import { z as
|
|
1424
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1425
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
|
|
1426
|
+
import { z as z19 } from "zod";
|
|
1247
1427
|
var MemDiffInputSchema = {
|
|
1248
|
-
id_a:
|
|
1249
|
-
id_b:
|
|
1428
|
+
id_a: z19.string().min(1).describe("First memory id"),
|
|
1429
|
+
id_b: z19.string().min(1).describe("Second memory id")
|
|
1250
1430
|
};
|
|
1251
1431
|
async function memDiff(input, ctx) {
|
|
1252
|
-
if (!
|
|
1432
|
+
if (!existsSync18(ctx.paths.memoriesDir)) {
|
|
1253
1433
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
1254
1434
|
}
|
|
1255
|
-
const all = await
|
|
1435
|
+
const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
1256
1436
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
1257
1437
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
1258
1438
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -1286,12 +1466,12 @@ async function memDiff(input, ctx) {
|
|
|
1286
1466
|
}
|
|
1287
1467
|
|
|
1288
1468
|
// src/prompts/bootstrap-project.ts
|
|
1289
|
-
import { z as
|
|
1469
|
+
import { z as z20 } from "zod";
|
|
1290
1470
|
var BootstrapProjectArgsSchema = {
|
|
1291
|
-
module:
|
|
1471
|
+
module: z20.string().optional().describe(
|
|
1292
1472
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
1293
1473
|
),
|
|
1294
|
-
focus:
|
|
1474
|
+
focus: z20.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
1295
1475
|
};
|
|
1296
1476
|
var ROOT_TEMPLATE = `# Project context
|
|
1297
1477
|
|
|
@@ -1373,10 +1553,10 @@ ${template}\`\`\`
|
|
|
1373
1553
|
}
|
|
1374
1554
|
|
|
1375
1555
|
// src/prompts/post-task.ts
|
|
1376
|
-
import { z as
|
|
1556
|
+
import { z as z21 } from "zod";
|
|
1377
1557
|
var PostTaskArgsSchema = {
|
|
1378
|
-
task_summary:
|
|
1379
|
-
files_touched:
|
|
1558
|
+
task_summary: z21.string().optional().describe("One sentence describing what you just did"),
|
|
1559
|
+
files_touched: z21.array(z21.string()).optional().describe("Files you created or modified during the task")
|
|
1380
1560
|
};
|
|
1381
1561
|
function postTaskPrompt(args, ctx) {
|
|
1382
1562
|
const taskLine = args.task_summary ? `
|
|
@@ -1433,7 +1613,18 @@ Examples of things to look for:
|
|
|
1433
1613
|
- Skip sections where you genuinely have nothing to add. Don't fabricate memories.
|
|
1434
1614
|
- **Question 0 is not optional** \u2014 always scan your exploration history for code-level discoveries.
|
|
1435
1615
|
|
|
1436
|
-
|
|
1616
|
+
### 6. Close the session \u2014 always
|
|
1617
|
+
Call **\`mem_session_end\`** with:
|
|
1618
|
+
- \`goal\`: what you set out to do
|
|
1619
|
+
- \`accomplished\`: what was actually done (bullet list)
|
|
1620
|
+
- \`discoveries\`: anything surprising or broken found during this session (leave empty if none)
|
|
1621
|
+
- \`files_touched\`: the key files you read or modified
|
|
1622
|
+
- \`next_steps\`: what remains for the next session or a teammate
|
|
1623
|
+
- \`scope\`: "team" if this task affects the whole team, "personal" otherwise
|
|
1624
|
+
|
|
1625
|
+
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.
|
|
1626
|
+
|
|
1627
|
+
When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved."
|
|
1437
1628
|
`;
|
|
1438
1629
|
return {
|
|
1439
1630
|
description: "Post-task reflection: capture what you learned before closing the session",
|
|
@@ -1447,12 +1638,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]" or "N
|
|
|
1447
1638
|
}
|
|
1448
1639
|
|
|
1449
1640
|
// src/prompts/import-docs.ts
|
|
1450
|
-
import { z as
|
|
1641
|
+
import { z as z22 } from "zod";
|
|
1451
1642
|
var ImportDocsArgsSchema = {
|
|
1452
|
-
content:
|
|
1453
|
-
source:
|
|
1454
|
-
scope:
|
|
1455
|
-
dry_run:
|
|
1643
|
+
content: z22.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
1644
|
+
source: z22.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
1645
|
+
scope: z22.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
1646
|
+
dry_run: z22.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
1456
1647
|
};
|
|
1457
1648
|
function importDocsPrompt(args, ctx) {
|
|
1458
1649
|
const sourceLine = args.source ? `
|
|
@@ -1517,7 +1708,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
1517
1708
|
|
|
1518
1709
|
// src/server.ts
|
|
1519
1710
|
var SERVER_NAME = "haive";
|
|
1520
|
-
var SERVER_VERSION = "0.2.
|
|
1711
|
+
var SERVER_VERSION = "0.2.12";
|
|
1521
1712
|
function jsonResult(data) {
|
|
1522
1713
|
return {
|
|
1523
1714
|
content: [
|
|
@@ -1642,6 +1833,12 @@ function createHaiveServer(options = {}) {
|
|
|
1642
1833
|
MemObserveInputSchema,
|
|
1643
1834
|
async (input) => jsonResult(await memObserve(input, context))
|
|
1644
1835
|
);
|
|
1836
|
+
server.tool(
|
|
1837
|
+
"mem_session_end",
|
|
1838
|
+
"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.",
|
|
1839
|
+
MemSessionEndInputSchema,
|
|
1840
|
+
async (input) => jsonResult(await memSessionEnd(input, context))
|
|
1841
|
+
);
|
|
1645
1842
|
server.prompt(
|
|
1646
1843
|
"bootstrap_project",
|
|
1647
1844
|
"Instructions for the AI client to analyze the project and save the context.",
|