@hiveai/mcp 0.3.0 → 0.3.2
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/README.md +44 -4
- package/dist/index.js +151 -21
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +13 -0
- package/dist/server.js +151 -21
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.d.ts
CHANGED
|
@@ -10,11 +10,24 @@ interface CreateContextOptions {
|
|
|
10
10
|
cwd?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
declare class SessionTracker {
|
|
14
|
+
private events;
|
|
15
|
+
private startedAt;
|
|
16
|
+
private config;
|
|
17
|
+
private ctx;
|
|
18
|
+
private shutdownRegistered;
|
|
19
|
+
constructor(ctx: HaiveContext);
|
|
20
|
+
init(): Promise<void>;
|
|
21
|
+
record(tool: string, summary?: string): void;
|
|
22
|
+
private registerShutdownHandler;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
declare const SERVER_NAME = "haive";
|
|
14
26
|
declare const SERVER_VERSION: string;
|
|
15
27
|
declare function createHaiveServer(options?: CreateContextOptions): {
|
|
16
28
|
server: McpServer;
|
|
17
29
|
context: HaiveContext;
|
|
30
|
+
tracker: SessionTracker;
|
|
18
31
|
};
|
|
19
32
|
|
|
20
33
|
export { SERVER_NAME, SERVER_VERSION, createHaiveServer };
|
package/dist/server.js
CHANGED
|
@@ -125,6 +125,7 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
125
125
|
import path3 from "path";
|
|
126
126
|
import {
|
|
127
127
|
buildFrontmatter,
|
|
128
|
+
loadConfig,
|
|
128
129
|
loadMemoriesFromDir as loadMemoriesFromDir2,
|
|
129
130
|
memoryFilePath,
|
|
130
131
|
serializeMemory
|
|
@@ -202,10 +203,12 @@ async function memSave(input, ctx) {
|
|
|
202
203
|
};
|
|
203
204
|
}
|
|
204
205
|
}
|
|
206
|
+
const haiveConfig = await loadConfig(ctx.paths);
|
|
207
|
+
const resolvedScope = input.scope !== "personal" ? input.scope : haiveConfig.defaultScope ?? "personal";
|
|
205
208
|
const frontmatter = buildFrontmatter({
|
|
206
209
|
type: input.type,
|
|
207
210
|
slug: input.slug,
|
|
208
|
-
scope:
|
|
211
|
+
scope: resolvedScope,
|
|
209
212
|
module: input.module,
|
|
210
213
|
tags: input.tags,
|
|
211
214
|
domain: input.domain,
|
|
@@ -213,7 +216,8 @@ async function memSave(input, ctx) {
|
|
|
213
216
|
paths: input.paths,
|
|
214
217
|
symbols: input.symbols,
|
|
215
218
|
commit: input.commit,
|
|
216
|
-
topic: input.topic
|
|
219
|
+
topic: input.topic,
|
|
220
|
+
status: haiveConfig.defaultStatus === "validated" ? "validated" : void 0
|
|
217
221
|
});
|
|
218
222
|
const file = memoryFilePath(
|
|
219
223
|
ctx.paths,
|
|
@@ -1160,6 +1164,7 @@ import {
|
|
|
1160
1164
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
1161
1165
|
literalMatchesAnyToken as literalMatchesAnyToken2,
|
|
1162
1166
|
loadCodeMap,
|
|
1167
|
+
loadConfig as loadConfig2,
|
|
1163
1168
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
1164
1169
|
loadUsageIndex as loadUsageIndex7,
|
|
1165
1170
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
@@ -1317,17 +1322,55 @@ async function getBriefing(input, ctx) {
|
|
|
1317
1322
|
}
|
|
1318
1323
|
const projectContextRaw = input.include_project_context && existsSync17(ctx.paths.projectContext) ? await readFile3(ctx.paths.projectContext, "utf8") : "";
|
|
1319
1324
|
const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
|
|
1320
|
-
const projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1321
1325
|
const setupWarnings = [];
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
);
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1326
|
+
let autoContextGenerated = false;
|
|
1327
|
+
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1328
|
+
if ((isTemplateContext || !existsSync17(ctx.paths.projectContext)) && input.include_project_context) {
|
|
1329
|
+
const haiveConfig = await loadConfig2(ctx.paths);
|
|
1330
|
+
if (haiveConfig.autoContext) {
|
|
1331
|
+
const codeMap = await loadCodeMap(ctx.paths);
|
|
1332
|
+
if (codeMap) {
|
|
1333
|
+
const totalFiles = Object.keys(codeMap.files).length;
|
|
1334
|
+
const extensions = /* @__PURE__ */ new Map();
|
|
1335
|
+
for (const filePath of Object.keys(codeMap.files)) {
|
|
1336
|
+
const ext = filePath.slice(filePath.lastIndexOf(".") + 1).toLowerCase();
|
|
1337
|
+
extensions.set(ext, (extensions.get(ext) ?? 0) + 1);
|
|
1338
|
+
}
|
|
1339
|
+
const topExts = [...extensions.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([e, n]) => `${e} (${n})`).join(", ");
|
|
1340
|
+
const topSymbols = Object.entries(codeMap.files).flatMap(
|
|
1341
|
+
([fp, entry]) => entry.exports.slice(0, 3).map((e) => `${e.name} (${fp.split("/").slice(-2).join("/")})`)
|
|
1342
|
+
).slice(0, 15).join(", ");
|
|
1343
|
+
projectContext = `# Project context (auto-generated by hAIve)
|
|
1344
|
+
|
|
1345
|
+
> \u26A0 This is a minimal auto-generated context based on the code-map. Invoke the \`bootstrap_project\` MCP prompt to replace it with a full analysis.
|
|
1346
|
+
|
|
1347
|
+
## Codebase overview
|
|
1348
|
+
- **${totalFiles} files** indexed in code-map
|
|
1349
|
+
- **Main file types:** ${topExts}
|
|
1350
|
+
- **Generated at:** ${codeMap.generated_at}
|
|
1351
|
+
|
|
1352
|
+
## Key exports (sample)
|
|
1353
|
+
` + topSymbols + "\n";
|
|
1354
|
+
autoContextGenerated = true;
|
|
1355
|
+
setupWarnings.push(
|
|
1356
|
+
"project-context.md is still the default template. A minimal auto-generated context has been injected from the code-map. Invoke bootstrap_project to replace it with a full AI-analyzed context."
|
|
1357
|
+
);
|
|
1358
|
+
} else {
|
|
1359
|
+
setupWarnings.push(
|
|
1360
|
+
"project-context.md is still the default template and no code-map found. Run `haive index code` then invoke bootstrap_project for a full context."
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
} else {
|
|
1364
|
+
if (isTemplateContext) {
|
|
1365
|
+
setupWarnings.push(
|
|
1366
|
+
"project-context.md still contains the default template. Invoke the bootstrap_project MCP prompt to auto-fill it from your codebase. Until then, get_briefing returns no project context."
|
|
1367
|
+
);
|
|
1368
|
+
} else {
|
|
1369
|
+
setupWarnings.push(
|
|
1370
|
+
"No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1331
1374
|
}
|
|
1332
1375
|
const moduleContents = input.include_module_contexts ? await loadModuleContexts2(ctx, inferred) : [];
|
|
1333
1376
|
const memoriesText = memories.map((m) => {
|
|
@@ -1426,10 +1469,11 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1426
1469
|
search_mode: searchMode,
|
|
1427
1470
|
inferred_modules: inferred,
|
|
1428
1471
|
...lastSession ? { last_session: lastSession } : {},
|
|
1429
|
-
project_context: projectContextRaw ? {
|
|
1472
|
+
project_context: projectContextRaw || autoContextGenerated ? {
|
|
1430
1473
|
content: projectSlice.text,
|
|
1431
1474
|
truncated: projectSlice.truncated,
|
|
1432
|
-
...isTemplateContext ? { is_template: true } : {}
|
|
1475
|
+
...isTemplateContext && !autoContextGenerated ? { is_template: true } : {},
|
|
1476
|
+
...autoContextGenerated ? { auto_generated: true } : {}
|
|
1433
1477
|
} : null,
|
|
1434
1478
|
module_contexts: trimmedModules,
|
|
1435
1479
|
memories: outputMemories,
|
|
@@ -1799,9 +1843,78 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
1799
1843
|
};
|
|
1800
1844
|
}
|
|
1801
1845
|
|
|
1846
|
+
// src/session-tracker.ts
|
|
1847
|
+
import { loadConfig as loadConfig3 } from "@hiveai/core";
|
|
1848
|
+
var SessionTracker = class {
|
|
1849
|
+
events = [];
|
|
1850
|
+
startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1851
|
+
config = null;
|
|
1852
|
+
ctx;
|
|
1853
|
+
shutdownRegistered = false;
|
|
1854
|
+
constructor(ctx) {
|
|
1855
|
+
this.ctx = ctx;
|
|
1856
|
+
}
|
|
1857
|
+
async init() {
|
|
1858
|
+
this.config = await loadConfig3(this.ctx.paths);
|
|
1859
|
+
if (this.config.autoSessionEnd) {
|
|
1860
|
+
this.registerShutdownHandler();
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
record(tool, summary) {
|
|
1864
|
+
this.events.push({ tool, at: (/* @__PURE__ */ new Date()).toISOString(), summary });
|
|
1865
|
+
}
|
|
1866
|
+
registerShutdownHandler() {
|
|
1867
|
+
if (this.shutdownRegistered) return;
|
|
1868
|
+
this.shutdownRegistered = true;
|
|
1869
|
+
const save = async () => {
|
|
1870
|
+
const writingTools = this.events.filter(
|
|
1871
|
+
(e) => ["mem_save", "mem_tried", "mem_observe", "mem_update", "bootstrap_project_save"].includes(e.tool)
|
|
1872
|
+
);
|
|
1873
|
+
const totalCalls = this.events.length;
|
|
1874
|
+
if (totalCalls === 0) return;
|
|
1875
|
+
const toolSummary = summarizeTools(this.events);
|
|
1876
|
+
const filesSet = /* @__PURE__ */ new Set();
|
|
1877
|
+
for (const e of this.events) {
|
|
1878
|
+
if (e.summary) {
|
|
1879
|
+
const matches = e.summary.match(/[^\s"',]+\.[a-zA-Z]{1,6}/g) ?? [];
|
|
1880
|
+
for (const m of matches) filesSet.add(m);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
try {
|
|
1884
|
+
await memSessionEnd(
|
|
1885
|
+
{
|
|
1886
|
+
goal: `Auto-captured session (${totalCalls} tool call${totalCalls === 1 ? "" : "s"})`,
|
|
1887
|
+
accomplished: toolSummary,
|
|
1888
|
+
discoveries: writingTools.length > 0 ? `${writingTools.length} memor${writingTools.length === 1 ? "y" : "ies"} saved during this session.` : "No new memories saved this session.",
|
|
1889
|
+
files_touched: [...filesSet].slice(0, 10),
|
|
1890
|
+
next_steps: "",
|
|
1891
|
+
scope: this.config?.defaultScope ?? "personal",
|
|
1892
|
+
module: void 0
|
|
1893
|
+
},
|
|
1894
|
+
this.ctx
|
|
1895
|
+
);
|
|
1896
|
+
} catch {
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
process.once("SIGTERM", () => {
|
|
1900
|
+
void save().finally(() => process.exit(0));
|
|
1901
|
+
});
|
|
1902
|
+
process.once("SIGINT", () => {
|
|
1903
|
+
void save().finally(() => process.exit(0));
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
function summarizeTools(events) {
|
|
1908
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1909
|
+
for (const e of events) {
|
|
1910
|
+
counts.set(e.tool, (counts.get(e.tool) ?? 0) + 1);
|
|
1911
|
+
}
|
|
1912
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} \xD7${n}`).join(", ");
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1802
1915
|
// src/server.ts
|
|
1803
1916
|
var SERVER_NAME = "haive";
|
|
1804
|
-
var SERVER_VERSION = "0.3.
|
|
1917
|
+
var SERVER_VERSION = "0.3.2";
|
|
1805
1918
|
function jsonResult(data) {
|
|
1806
1919
|
return {
|
|
1807
1920
|
content: [
|
|
@@ -1814,6 +1927,8 @@ function jsonResult(data) {
|
|
|
1814
1927
|
}
|
|
1815
1928
|
function createHaiveServer(options = {}) {
|
|
1816
1929
|
const context = createContext(options);
|
|
1930
|
+
const tracker = new SessionTracker(context);
|
|
1931
|
+
void tracker.init();
|
|
1817
1932
|
const server = new McpServer(
|
|
1818
1933
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
1819
1934
|
{ capabilities: { tools: {}, prompts: {} } }
|
|
@@ -1822,7 +1937,10 @@ function createHaiveServer(options = {}) {
|
|
|
1822
1937
|
"mem_save",
|
|
1823
1938
|
"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.",
|
|
1824
1939
|
MemSaveInputSchema,
|
|
1825
|
-
async (input) =>
|
|
1940
|
+
async (input) => {
|
|
1941
|
+
tracker.record("mem_save", input.slug);
|
|
1942
|
+
return jsonResult(await memSave(input, context));
|
|
1943
|
+
}
|
|
1826
1944
|
);
|
|
1827
1945
|
server.tool(
|
|
1828
1946
|
"mem_search",
|
|
@@ -1846,7 +1964,10 @@ function createHaiveServer(options = {}) {
|
|
|
1846
1964
|
"get_briefing",
|
|
1847
1965
|
"One-shot onboarding: returns project context + module contexts + ranked relevant memories under a token budget. Replaces 4\u20135 separate calls when an agent starts a task.",
|
|
1848
1966
|
GetBriefingInputSchema,
|
|
1849
|
-
async (input) =>
|
|
1967
|
+
async (input) => {
|
|
1968
|
+
tracker.record("get_briefing", input.task ?? "");
|
|
1969
|
+
return jsonResult(await getBriefing(input, context));
|
|
1970
|
+
}
|
|
1850
1971
|
);
|
|
1851
1972
|
server.tool(
|
|
1852
1973
|
"code_map",
|
|
@@ -1912,7 +2033,10 @@ function createHaiveServer(options = {}) {
|
|
|
1912
2033
|
"mem_tried",
|
|
1913
2034
|
"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.",
|
|
1914
2035
|
MemTriedInputSchema,
|
|
1915
|
-
async (input) =>
|
|
2036
|
+
async (input) => {
|
|
2037
|
+
tracker.record("mem_tried", input.what.slice(0, 80));
|
|
2038
|
+
return jsonResult(await memTried(input, context));
|
|
2039
|
+
}
|
|
1916
2040
|
);
|
|
1917
2041
|
server.tool(
|
|
1918
2042
|
"mem_diff",
|
|
@@ -1924,13 +2048,19 @@ function createHaiveServer(options = {}) {
|
|
|
1924
2048
|
"mem_observe",
|
|
1925
2049
|
"Capture a code-level discovery made during exploration: a bug, inconsistency, missing config, or security gap found by reading existing code that was NOT in the briefing. Use this whenever you read code and spot something that could silently break in production. Auto-validated, anchored to file paths for staleness detection. Prefer this over mem_save for reactive discoveries during code reading.",
|
|
1926
2050
|
MemObserveInputSchema,
|
|
1927
|
-
async (input) =>
|
|
2051
|
+
async (input) => {
|
|
2052
|
+
tracker.record("mem_observe", input.where);
|
|
2053
|
+
return jsonResult(await memObserve(input, context));
|
|
2054
|
+
}
|
|
1928
2055
|
);
|
|
1929
2056
|
server.tool(
|
|
1930
2057
|
"mem_session_end",
|
|
1931
2058
|
"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.",
|
|
1932
2059
|
MemSessionEndInputSchema,
|
|
1933
|
-
async (input) =>
|
|
2060
|
+
async (input) => {
|
|
2061
|
+
tracker.record("mem_session_end", input.goal.slice(0, 80));
|
|
2062
|
+
return jsonResult(await memSessionEnd(input, context));
|
|
2063
|
+
}
|
|
1934
2064
|
);
|
|
1935
2065
|
server.prompt(
|
|
1936
2066
|
"bootstrap_project",
|
|
@@ -1950,7 +2080,7 @@ function createHaiveServer(options = {}) {
|
|
|
1950
2080
|
ImportDocsArgsSchema,
|
|
1951
2081
|
(args) => importDocsPrompt(args, context)
|
|
1952
2082
|
);
|
|
1953
|
-
return { server, context };
|
|
2083
|
+
return { server, context, tracker };
|
|
1954
2084
|
}
|
|
1955
2085
|
export {
|
|
1956
2086
|
SERVER_NAME,
|