@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/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: input.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
- if (isTemplateContext) {
1323
- setupWarnings.push(
1324
- "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."
1325
- );
1326
- }
1327
- if (!existsSync17(ctx.paths.projectContext)) {
1328
- setupWarnings.push(
1329
- "No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
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.0";
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) => jsonResult(await memSave(input, context))
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) => jsonResult(await getBriefing(input, context))
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) => jsonResult(await memTried(input, context))
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) => jsonResult(await memObserve(input, context))
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) => jsonResult(await memSessionEnd(input, context))
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,