@hiveai/mcp 0.9.3 → 0.9.6

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
@@ -1,5 +1,6 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { HaivePaths, ConfidenceLevel } from '@hiveai/core';
2
+ import * as _hiveai_core from '@hiveai/core';
3
+ import { HaivePaths, ConfidenceLevel, resolveProjectInfo, readRuntimeJournalTail } from '@hiveai/core';
3
4
  import { z } from 'zod';
4
5
 
5
6
  interface HaiveContext {
@@ -565,6 +566,88 @@ interface PatternDetectOutput {
565
566
  }
566
567
  declare function patternDetect(input: PatternDetectInput, ctx: HaiveContext): Promise<PatternDetectOutput>;
567
568
 
569
+ /** Input is intentionally minimal — callers may pass cwd for multi-root clients. */
570
+ declare const MemResolveProjectInputSchema: {
571
+ cwd: z.ZodOptional<z.ZodString>;
572
+ };
573
+ type MemResolveProjectInput = {
574
+ [K in keyof typeof MemResolveProjectInputSchema]: z.infer<(typeof MemResolveProjectInputSchema)[K]>;
575
+ };
576
+ declare function memResolveProject(input: MemResolveProjectInput, _ctx: HaiveContext): Promise<{
577
+ info: ReturnType<typeof resolveProjectInfo>;
578
+ ok: true;
579
+ }>;
580
+
581
+ declare const MemSuggestTopicInputSchema: {
582
+ type: z.ZodEnum<["convention", "decision", "gotcha", "architecture", "glossary", "attempt", "session_recap"]>;
583
+ title: z.ZodString;
584
+ };
585
+ type MemSuggestTopicInput = {
586
+ [K in keyof typeof MemSuggestTopicInputSchema]: z.infer<(typeof MemSuggestTopicInputSchema)[K]>;
587
+ };
588
+ declare function memSuggestTopic(input: MemSuggestTopicInput, _ctx: HaiveContext): Promise<{
589
+ topic_key: string;
590
+ family: string;
591
+ type: string;
592
+ }>;
593
+
594
+ declare const MemTimelineInputSchema: {
595
+ memory_id: z.ZodOptional<z.ZodString>;
596
+ topic: z.ZodOptional<z.ZodString>;
597
+ limit: z.ZodDefault<z.ZodNumber>;
598
+ };
599
+ type MemTimelineInput = {
600
+ [K in keyof typeof MemTimelineInputSchema]: z.infer<(typeof MemTimelineInputSchema)[K]>;
601
+ };
602
+ declare function memTimeline(input: MemTimelineInput, ctx: HaiveContext): Promise<{
603
+ entries: _hiveai_core.TimelineEntry[];
604
+ total: number;
605
+ notice: string | undefined;
606
+ }>;
607
+
608
+ declare const MemConflictCandidatesInputSchema: {
609
+ since_days: z.ZodDefault<z.ZodNumber>;
610
+ types: z.ZodDefault<z.ZodArray<z.ZodEnum<["decision", "architecture", "convention", "gotcha"]>, "many">>;
611
+ min_jaccard: z.ZodDefault<z.ZodNumber>;
612
+ max_pairs: z.ZodDefault<z.ZodNumber>;
613
+ max_scan: z.ZodDefault<z.ZodNumber>;
614
+ max_topic_pairs: z.ZodDefault<z.ZodNumber>;
615
+ };
616
+ type MemConflictCandidatesInput = {
617
+ [K in keyof typeof MemConflictCandidatesInputSchema]: z.infer<(typeof MemConflictCandidatesInputSchema)[K]>;
618
+ };
619
+ declare function memConflictCandidates(input: MemConflictCandidatesInput, ctx: HaiveContext): Promise<{
620
+ pairs: _hiveai_core.ConflictCandidatePair[];
621
+ topic_status_pairs: _hiveai_core.TopicStatusPair[];
622
+ scanned: number;
623
+ truncated: boolean;
624
+ notice: string | undefined;
625
+ }>;
626
+
627
+ declare const RuntimeJournalAppendInputSchema: {
628
+ message: z.ZodString;
629
+ kind: z.ZodDefault<z.ZodEnum<["note", "session_end", "mcp"]>>;
630
+ tool: z.ZodOptional<z.ZodString>;
631
+ };
632
+ type RuntimeJournalAppendInput = {
633
+ [K in keyof typeof RuntimeJournalAppendInputSchema]: z.infer<(typeof RuntimeJournalAppendInputSchema)[K]>;
634
+ };
635
+ declare function runtimeJournalAppend(input: RuntimeJournalAppendInput, ctx: HaiveContext): Promise<{
636
+ ok: true;
637
+ path_hint: string;
638
+ }>;
639
+
640
+ declare const RuntimeJournalTailInputSchema: {
641
+ limit: z.ZodDefault<z.ZodNumber>;
642
+ };
643
+ type RuntimeJournalTailInput = {
644
+ [K in keyof typeof RuntimeJournalTailInputSchema]: z.infer<(typeof RuntimeJournalTailInputSchema)[K]>;
645
+ };
646
+ declare function runtimeJournalTail(input: RuntimeJournalTailInput, ctx: HaiveContext): Promise<{
647
+ entries: Awaited<ReturnType<typeof readRuntimeJournalTail>>;
648
+ empty?: boolean;
649
+ }>;
650
+
568
651
  declare const SERVER_NAME = "haive";
569
652
  declare const SERVER_VERSION: string;
570
653
  declare function createHaiveServer(options?: CreateContextOptions): {
@@ -587,4 +670,4 @@ declare function runHaiveMcpStdio(options: {
587
670
  root?: string;
588
671
  }): Promise<void>;
589
672
 
590
- export { type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, type MemConflictsInput, type MemConflictsOutput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type PatternDetectInput, type PatternDetectOutput, type PreCommitCheckInput, type PreCommitCheckOutput, SERVER_NAME, SERVER_VERSION, type WhyThisDecisionInput, type WhyThisDecisionOutput, type WhyThisFileInput, type WhyThisFileOutput, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, getBriefing, getRecap, memConflicts, memDistill, memRelevantTo, parseMcpCliArgs, patternDetect, preCommitCheck, printHaiveMcpVersion, runHaiveMcpStdio, whyThisDecision, whyThisFile };
673
+ export { type AntiPatternsCheckInput, type AntiPatternsCheckOutput, type BriefingOutput, type CodeMapInput, type CodeMapToolOutput, type CodeSearchInput, type CodeSearchOutput, type GetBriefingInput, type GetRecapInput, type GetRecapOutput, type MemConflictCandidatesInput, type MemConflictsInput, type MemConflictsOutput, type MemDistillInput, type MemDistillOutput, type MemRelevantToInput, type MemRelevantToOutput, type MemResolveProjectInput, type MemSuggestTopicInput, type MemTimelineInput, type PatternDetectInput, type PatternDetectOutput, type PreCommitCheckInput, type PreCommitCheckOutput, type RuntimeJournalAppendInput, type RuntimeJournalTailInput, SERVER_NAME, SERVER_VERSION, type WhyThisDecisionInput, type WhyThisDecisionOutput, type WhyThisFileInput, type WhyThisFileOutput, antiPatternsCheck, codeMapTool, codeSearch, createHaiveServer, getBriefing, getRecap, memConflictCandidates, memConflicts, memDistill, memRelevantTo, memResolveProject, memSuggestTopic, memTimeline, parseMcpCliArgs, patternDetect, preCommitCheck, printHaiveMcpVersion, runHaiveMcpStdio, runtimeJournalAppend, runtimeJournalTail, whyThisDecision, whyThisFile };
package/dist/server.js CHANGED
@@ -319,6 +319,7 @@ import {
319
319
  loadMemoriesFromDir as loadMemoriesFromDir3,
320
320
  loadUsageIndex,
321
321
  pickSnippetNeedle,
322
+ rankMemoriesLexical,
322
323
  tokenizeQuery,
323
324
  trackReads
324
325
  } from "@hiveai/core";
@@ -335,6 +336,9 @@ var MemSearchInputSchema = {
335
336
  semantic: z5.boolean().default(false).describe(
336
337
  "Use semantic similarity from the embeddings index (requires `haive embeddings index`)."
337
338
  ),
339
+ lexical_rank: z5.boolean().default(false).describe(
340
+ "When true (and semantic is false), rank the filtered corpus with Okapi-BM25-style lexical scoring instead of literal AND/OR. Helps phrase-like queries without embeddings."
341
+ ),
338
342
  min_score: z5.number().min(0).max(1).default(0).describe("Minimum cosine similarity (semantic mode only)"),
339
343
  track: z5.boolean().default(true).describe("Increment read_count on returned memories (used for passive validation)")
340
344
  };
@@ -360,6 +364,27 @@ async function memSearch(input, ctx) {
360
364
  notice: "Semantic search unavailable (embeddings index missing or @hiveai/embeddings not installed). Falling back to literal search."
361
365
  };
362
366
  }
367
+ } else if (input.lexical_rank && input.query.trim()) {
368
+ const { ranked, scores } = rankMemoriesLexical(
369
+ filtered,
370
+ input.query,
371
+ input.limit
372
+ );
373
+ if (ranked.length > 0) {
374
+ const snippetNeedle = pickSnippetNeedle(input.query);
375
+ result = {
376
+ matches: ranked.map(
377
+ (loaded, i) => lexicalHit(loaded, snippetNeedle, usage, scores[i])
378
+ ),
379
+ total: ranked.length,
380
+ mode: "lexical_ranked"
381
+ };
382
+ } else {
383
+ result = {
384
+ ...buildLiteralResult(input, filtered, usage),
385
+ notice: "Lexical ranking found no BM25-positive hits \u2014 showing literal matches instead."
386
+ };
387
+ }
363
388
  } else {
364
389
  result = buildLiteralResult(input, filtered, usage);
365
390
  }
@@ -457,6 +482,9 @@ function toHit(loaded, needle, usage) {
457
482
  file_path: loaded.filePath
458
483
  };
459
484
  }
485
+ function lexicalHit(loaded, needle, usage, lexicalScore) {
486
+ return { ...toHit(loaded, needle, usage), score: lexicalScore };
487
+ }
460
488
 
461
489
  // src/tools/mem-verify.ts
462
490
  import { writeFile as writeFile3 } from "fs/promises";
@@ -1097,7 +1125,11 @@ import {
1097
1125
  import { z as z16 } from "zod";
1098
1126
 
1099
1127
  // src/session-tracker.ts
1100
- import { appendUsageEvent, loadConfig as loadConfig2 } from "@hiveai/core";
1128
+ import {
1129
+ appendUsageEvent,
1130
+ appendRuntimeJournalEntry,
1131
+ loadConfig as loadConfig2
1132
+ } from "@hiveai/core";
1101
1133
  import { mkdir as mkdir5, writeFile as writeFile9, rm } from "fs/promises";
1102
1134
  import { existsSync as existsSync16 } from "fs";
1103
1135
  import path7 from "path";
@@ -1170,6 +1202,14 @@ var SessionTracker = class {
1170
1202
  recapId = result.id;
1171
1203
  } catch {
1172
1204
  }
1205
+ void appendRuntimeJournalEntry(this.ctx.paths, {
1206
+ kind: "session_end",
1207
+ message: recapId ? `auto session close | ${toolSummary} | recap:${recapId}` : `auto session close | ${toolSummary}`,
1208
+ meta: {
1209
+ recap_id: recapId ?? null,
1210
+ total_tool_calls: totalCalls
1211
+ }
1212
+ });
1173
1213
  const ranPostTask = this.events.some(
1174
1214
  (e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
1175
1215
  );
@@ -3038,13 +3078,139 @@ function gitFileDiff(root, file, sinceDays) {
3038
3078
  }
3039
3079
  }
3040
3080
 
3041
- // src/prompts/bootstrap-project.ts
3081
+ // src/tools/mem-conflict-candidates.ts
3082
+ import { existsSync as existsSync27 } from "fs";
3083
+ import {
3084
+ findLexicalConflictPairs,
3085
+ findTopicStatusConflictPairs,
3086
+ loadMemoriesFromDir as loadMemoriesFromDir21
3087
+ } from "@hiveai/core";
3042
3088
  import { z as z30 } from "zod";
3089
+ var MemConflictCandidatesInputSchema = {
3090
+ since_days: z30.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3091
+ types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
3092
+ min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
3093
+ max_pairs: z30.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
3094
+ max_scan: z30.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
3095
+ max_topic_pairs: z30.number().int().positive().max(100).default(20).describe(
3096
+ "Cap for extra signal: memories sharing the same topic with validated vs rejected status."
3097
+ )
3098
+ };
3099
+ async function memConflictCandidates(input, ctx) {
3100
+ if (!existsSync27(ctx.paths.memoriesDir)) {
3101
+ return {
3102
+ pairs: [],
3103
+ topic_status_pairs: [],
3104
+ scanned: 0,
3105
+ truncated: false,
3106
+ notice: "No .ai/memories directory."
3107
+ };
3108
+ }
3109
+ const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
3110
+ const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
3111
+ sinceDays: input.since_days,
3112
+ types: input.types,
3113
+ minJaccard: input.min_jaccard,
3114
+ maxPairs: input.max_pairs,
3115
+ maxScan: input.max_scan
3116
+ });
3117
+ const topicStatusPairs = findTopicStatusConflictPairs(all, input.max_topic_pairs);
3118
+ const notice = pairs.length === 0 && topicStatusPairs.length === 0 ? "No lexical or topic-status candidates \u2014 widen since_days/types or lower min_jaccard." : void 0;
3119
+ return { pairs, topic_status_pairs: topicStatusPairs, scanned, truncated, notice };
3120
+ }
3121
+
3122
+ // src/tools/mem-resolve-project.ts
3123
+ import { resolveProjectInfo } from "@hiveai/core";
3124
+ import { z as z31 } from "zod";
3125
+ var MemResolveProjectInputSchema = {
3126
+ cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
3127
+ };
3128
+ async function memResolveProject(input, _ctx) {
3129
+ void _ctx;
3130
+ return {
3131
+ ok: true,
3132
+ info: resolveProjectInfo({
3133
+ cwd: input.cwd
3134
+ })
3135
+ };
3136
+ }
3137
+
3138
+ // src/tools/mem-suggest-topic.ts
3139
+ import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3140
+ import { z as z32 } from "zod";
3141
+ var MemSuggestTopicInputSchema = {
3142
+ type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
3143
+ title: z32.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
3144
+ };
3145
+ async function memSuggestTopic(input, _ctx) {
3146
+ void _ctx;
3147
+ const suggestion = suggestTopicKey(input.type, input.title);
3148
+ return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
3149
+ }
3150
+
3151
+ // src/tools/mem-timeline.ts
3152
+ import { existsSync as existsSync28 } from "fs";
3153
+ import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hiveai/core";
3154
+ import { z as z33 } from "zod";
3155
+ var MemTimelineInputSchema = {
3156
+ memory_id: z33.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
3157
+ topic: z33.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
3158
+ limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
3159
+ };
3160
+ async function memTimeline(input, ctx) {
3161
+ if (!existsSync28(ctx.paths.memoriesDir)) {
3162
+ return { entries: [], total: 0, notice: "No .ai/memories directory." };
3163
+ }
3164
+ const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
3165
+ const { entries, notice } = collectTimelineEntries(all, {
3166
+ memoryId: input.memory_id,
3167
+ topic: input.topic,
3168
+ limit: input.limit
3169
+ });
3170
+ return { entries, total: entries.length, notice };
3171
+ }
3172
+
3173
+ // src/tools/runtime-journal-append.ts
3174
+ import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
3175
+ import { z as z34 } from "zod";
3176
+ var RuntimeJournalAppendInputSchema = {
3177
+ message: z34.string().min(1).describe("Short line to append to the runtime session journal"),
3178
+ kind: z34.enum(["note", "session_end", "mcp"]).default("note"),
3179
+ tool: z34.string().optional().describe("When kind=mcp, which tool name (optional)")
3180
+ };
3181
+ async function runtimeJournalAppend(input, ctx) {
3182
+ await appendRuntimeJournalEntry2(ctx.paths, {
3183
+ kind: input.kind,
3184
+ message: input.message,
3185
+ ...input.tool ? { tool: input.tool } : {}
3186
+ });
3187
+ return {
3188
+ ok: true,
3189
+ path_hint: `${ctx.paths.runtimeDir}/session-journal.ndjson`
3190
+ };
3191
+ }
3192
+
3193
+ // src/tools/runtime-journal-tail.ts
3194
+ import { readRuntimeJournalTail } from "@hiveai/core";
3195
+ import { z as z35 } from "zod";
3196
+ var RuntimeJournalTailInputSchema = {
3197
+ limit: z35.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
3198
+ };
3199
+ async function runtimeJournalTail(input, ctx) {
3200
+ const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
3201
+ if (entries.length === 0) {
3202
+ return { entries: [], empty: true };
3203
+ }
3204
+ return { entries };
3205
+ }
3206
+
3207
+ // src/prompts/bootstrap-project.ts
3208
+ import { z as z36 } from "zod";
3043
3209
  var BootstrapProjectArgsSchema = {
3044
- module: z30.string().optional().describe(
3210
+ module: z36.string().optional().describe(
3045
3211
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
3046
3212
  ),
3047
- focus: z30.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3213
+ focus: z36.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3048
3214
  };
3049
3215
  var ROOT_TEMPLATE = `# Project context
3050
3216
 
@@ -3126,10 +3292,10 @@ ${template}\`\`\`
3126
3292
  }
3127
3293
 
3128
3294
  // src/prompts/post-task.ts
3129
- import { z as z31 } from "zod";
3295
+ import { z as z37 } from "zod";
3130
3296
  var PostTaskArgsSchema = {
3131
- task_summary: z31.string().optional().describe("One sentence describing what you just did"),
3132
- files_touched: z31.array(z31.string()).optional().describe("Files you created or modified during the task")
3297
+ task_summary: z37.string().optional().describe("One sentence describing what you just did"),
3298
+ files_touched: z37.array(z37.string()).optional().describe("Files you created or modified during the task")
3133
3299
  };
3134
3300
  function postTaskPrompt(args, ctx) {
3135
3301
  const taskLine = args.task_summary ? `
@@ -3213,12 +3379,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
3213
3379
  }
3214
3380
 
3215
3381
  // src/prompts/import-docs.ts
3216
- import { z as z32 } from "zod";
3382
+ import { z as z38 } from "zod";
3217
3383
  var ImportDocsArgsSchema = {
3218
- content: z32.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
3219
- source: z32.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
3220
- scope: z32.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
3221
- dry_run: z32.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
3384
+ content: z38.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
3385
+ source: z38.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
3386
+ scope: z38.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
3387
+ dry_run: z38.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
3222
3388
  };
3223
3389
  function importDocsPrompt(args, ctx) {
3224
3390
  const sourceLine = args.source ? `
@@ -3283,7 +3449,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
3283
3449
 
3284
3450
  // src/server.ts
3285
3451
  var SERVER_NAME = "haive";
3286
- var SERVER_VERSION = "0.9.3";
3452
+ var SERVER_VERSION = "0.9.6";
3287
3453
  function jsonResult(data) {
3288
3454
  return {
3289
3455
  content: [
@@ -3334,6 +3500,23 @@ function createHaiveServer(options = {}) {
3334
3500
  return jsonResult(await memSave(input, context));
3335
3501
  }
3336
3502
  );
3503
+ server.tool(
3504
+ "mem_suggest_topic",
3505
+ [
3506
+ "Propose a stable `topic` key (topic-upsert) from type + short title.",
3507
+ "",
3508
+ "USE BEFORE mem_save when you want deterministic updates to the same memory over time;",
3509
+ "families mimic Engram-style grouping (architecture/*, bug/*, decision/*, \u2026).",
3510
+ "",
3511
+ "PARAMETERS:",
3512
+ " type \u2014 convention | decision | gotcha | architecture | glossary | attempt | session_recap",
3513
+ " title \u2014 phrase to slugify under the suggested family prefix",
3514
+ "",
3515
+ "RETURNS: { topic_key, family, type }"
3516
+ ].join("\n"),
3517
+ MemSuggestTopicInputSchema,
3518
+ async (input) => jsonResult(await memSuggestTopic(input, context))
3519
+ );
3337
3520
  server.tool(
3338
3521
  "mem_tried",
3339
3522
  [
@@ -3425,8 +3608,12 @@ function createHaiveServer(options = {}) {
3425
3608
  server.tool(
3426
3609
  "get_briefing",
3427
3610
  [
3428
- "\u2B50 CALL THIS FIRST at the start of every task. One-shot onboarding that returns",
3429
- "everything relevant in a single call under a token budget.",
3611
+ "\u2B50 DEFAULT-FIRST for coding agents on any repo where `haive init` ran: call this BEFORE",
3612
+ "changing source or project config for the current goal (unless the developer explicitly opts out).",
3613
+ "One-shot onboarding: everything relevant in a single call under a token budget.",
3614
+ "",
3615
+ "PROGRESSIVE DISCLOSURE \u2014 after this, drill down only if needed:",
3616
+ " mem_relevant_to / mem_search (compact lists) \u2192 mem_get (full body + anchors).",
3430
3617
  "",
3431
3618
  "RETURNS (in order of priority):",
3432
3619
  " 0. action_required \u2014 \u26A0\uFE0F HANDLE THIS FIRST if non-empty (see protocol below)",
@@ -3461,7 +3648,7 @@ function createHaiveServer(options = {}) {
3461
3648
  " low \u2014 proposed, few reads (take with caution)",
3462
3649
  " unverified \u2014 draft (unverified: true flag set)",
3463
3650
  "",
3464
- "Replaces 4\u20135 separate tool calls. Always call this before any other tool."
3651
+ "Replaces 4\u20135 separate tool calls. Prefer this first; use mem_search / mem_get only for follow-up."
3465
3652
  ].join("\n"),
3466
3653
  GetBriefingInputSchema,
3467
3654
  async (input) => {
@@ -3480,6 +3667,8 @@ function createHaiveServer(options = {}) {
3480
3667
  "SEARCH MODES:",
3481
3668
  " Literal (default): AND search across id, tags, and body \u2014 all tokens must match.",
3482
3669
  " Falls back to OR automatically if no AND results (partial match).",
3670
+ " Lexical rank (lexical_rank: true, semantic: false): Okapi-BM25-style scoring on the",
3671
+ " filtered corpus \u2014 good for phrase-like queries without embeddings.",
3483
3672
  " Semantic (semantic: true): embedding-based similarity \u2014 finds related memories",
3484
3673
  " even with different wording. Requires haive embeddings index to be built.",
3485
3674
  "",
@@ -3488,6 +3677,7 @@ function createHaiveServer(options = {}) {
3488
3677
  " scope \u2014 filter by personal | team | module",
3489
3678
  " type \u2014 filter by convention | decision | gotcha | architecture | glossary",
3490
3679
  " semantic \u2014 true for embedding-based search (requires @hiveai/embeddings)",
3680
+ " lexical_rank \u2014 BM25-style ranking (ignored when semantic is true)",
3491
3681
  " limit \u2014 max results (default 10)",
3492
3682
  "",
3493
3683
  "RETURNS: array of { id, type, scope, status, confidence, body, match_quality }"
@@ -3498,6 +3688,22 @@ function createHaiveServer(options = {}) {
3498
3688
  return jsonResult(await memSearch(input, context));
3499
3689
  }
3500
3690
  );
3691
+ server.tool(
3692
+ "mem_timeline",
3693
+ [
3694
+ "Chronological view of related memories: by shared frontmatter.topic OR expanded from a seed id",
3695
+ "(related_ids, same topic, overlapping anchor paths \u2014 one extra hop on related_ids).",
3696
+ "",
3697
+ "PARAMETERS:",
3698
+ " memory_id \u2014 optional seed memory id",
3699
+ " topic \u2014 optional topic key (required if memory_id omitted)",
3700
+ " limit \u2014 max entries (default 30)",
3701
+ "",
3702
+ "RETURNS: { entries: [{ id, type, scope, created_at, one_line, topic? }], total, notice? }"
3703
+ ].join("\n"),
3704
+ MemTimelineInputSchema,
3705
+ async (input) => jsonResult(await memTimeline(input, context))
3706
+ );
3501
3707
  server.tool(
3502
3708
  "mem_for_files",
3503
3709
  [
@@ -3527,7 +3733,7 @@ function createHaiveServer(options = {}) {
3527
3733
  [
3528
3734
  "Fetch a single memory by its full id with all details.",
3529
3735
  "",
3530
- "USE WHEN get_briefing returned a memory in 'compact' format and you need",
3736
+ "USE WHEN get_briefing / mem_relevant_to / mem_search returned a compact hit and you need",
3531
3737
  "the full body, or when you know the exact id of a memory.",
3532
3738
  "",
3533
3739
  "PARAMETERS:",
@@ -3615,6 +3821,22 @@ function createHaiveServer(options = {}) {
3615
3821
  CodeMapInputSchema,
3616
3822
  async (input) => jsonResult(await codeMapTool(input, context))
3617
3823
  );
3824
+ server.tool(
3825
+ "mem_resolve_project",
3826
+ [
3827
+ "Diagnostics: resolve which project root hAIve is using (never throws).",
3828
+ "",
3829
+ "USE IN multi-root workspaces or when the agent CWD may not be the repo root \u2014",
3830
+ "mirrors HAIVE_PROJECT_ROOT, findProjectRoot markers, and presence of .ai/memories.",
3831
+ "",
3832
+ "PARAMETERS:",
3833
+ " cwd \u2014 optional directory used for discovery when HAIVE_PROJECT_ROOT is unset",
3834
+ "",
3835
+ "RETURNS: { ok: true, info: { cwd, resolved_root, haive_project_root_env, \u2026 } }"
3836
+ ].join("\n"),
3837
+ MemResolveProjectInputSchema,
3838
+ async (input) => jsonResult(await memResolveProject(input, context))
3839
+ );
3618
3840
  server.tool(
3619
3841
  "mem_update",
3620
3842
  [
@@ -3748,6 +3970,8 @@ function createHaiveServer(options = {}) {
3748
3970
  "One-shot ranked memories for a task \u2014 use instead of get_briefing when",
3749
3971
  "project context is already loaded and you only want the relevant memory layer.",
3750
3972
  "",
3973
+ "Second step in progressive disclosure (after get_briefing): narrow here, then mem_get for full text.",
3974
+ "",
3751
3975
  "Reuses the same ranking pipeline (anchor / module / literal / semantic) but",
3752
3976
  "skips project_context, modules, action_required, etc.",
3753
3977
  "",
@@ -3905,6 +4129,53 @@ function createHaiveServer(options = {}) {
3905
4129
  return jsonResult(await memConflicts(input, context));
3906
4130
  }
3907
4131
  );
4132
+ server.tool(
4133
+ "mem_conflict_candidates",
4134
+ [
4135
+ "Bulk scan for conflict CANDIDATES (not proof):",
4136
+ "",
4137
+ " 1. Lexical similarity (Jaccard) on decision/architecture-like pairs",
4138
+ " 2. Same frontmatter.topic with validated vs rejected \u2014 quick human-review signal",
4139
+ "",
4140
+ "Advisory only \u2014 follow with mem_conflicts_with on specific ids.",
4141
+ "",
4142
+ "PARAMETERS:",
4143
+ " since_days, types, min_jaccard, max_pairs, max_scan, max_topic_pairs",
4144
+ "",
4145
+ "RETURNS: { pairs, topic_status_pairs, scanned, truncated, notice? }"
4146
+ ].join("\n"),
4147
+ MemConflictCandidatesInputSchema,
4148
+ async (input) => {
4149
+ tracker.record("mem_conflict_candidates", `${input.since_days}d`);
4150
+ return jsonResult(await memConflictCandidates(input, context));
4151
+ }
4152
+ );
4153
+ server.tool(
4154
+ "runtime_journal_append",
4155
+ [
4156
+ "Append one line to `.ai/.runtime/session-journal.ndjson` \u2014 machine-local session continuity.",
4157
+ "",
4158
+ "Does NOT replace team memories; complements mem_session_end recaps for local traces.",
4159
+ "",
4160
+ "PARAMETERS: message, kind (note|session_end|mcp), optional tool",
4161
+ "",
4162
+ "RETURNS: { ok, path_hint }"
4163
+ ].join("\n"),
4164
+ RuntimeJournalAppendInputSchema,
4165
+ async (input) => jsonResult(await runtimeJournalAppend(input, context))
4166
+ );
4167
+ server.tool(
4168
+ "runtime_journal_tail",
4169
+ [
4170
+ "Read the last N entries from the runtime session journal (parsed JSON lines).",
4171
+ "",
4172
+ "PARAMETERS: limit (default 30, max 500)",
4173
+ "",
4174
+ "RETURNS: { entries: [...], empty?: true }"
4175
+ ].join("\n"),
4176
+ RuntimeJournalTailInputSchema,
4177
+ async (input) => jsonResult(await runtimeJournalTail(input, context))
4178
+ );
3908
4179
  server.tool(
3909
4180
  "pre_commit_check",
3910
4181
  [
@@ -4051,14 +4322,20 @@ export {
4051
4322
  createHaiveServer,
4052
4323
  getBriefing,
4053
4324
  getRecap,
4325
+ memConflictCandidates,
4054
4326
  memConflicts,
4055
4327
  memDistill,
4056
4328
  memRelevantTo,
4329
+ memResolveProject,
4330
+ memSuggestTopic,
4331
+ memTimeline,
4057
4332
  parseMcpCliArgs,
4058
4333
  patternDetect,
4059
4334
  preCommitCheck,
4060
4335
  printHaiveMcpVersion,
4061
4336
  runHaiveMcpStdio,
4337
+ runtimeJournalAppend,
4338
+ runtimeJournalTail,
4062
4339
  whyThisDecision,
4063
4340
  whyThisFile
4064
4341
  };