@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/index.js +288 -17
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +85 -2
- package/dist/server.js +294 -17
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -321,6 +321,7 @@ import {
|
|
|
321
321
|
loadMemoriesFromDir as loadMemoriesFromDir3,
|
|
322
322
|
loadUsageIndex,
|
|
323
323
|
pickSnippetNeedle,
|
|
324
|
+
rankMemoriesLexical,
|
|
324
325
|
tokenizeQuery,
|
|
325
326
|
trackReads
|
|
326
327
|
} from "@hiveai/core";
|
|
@@ -337,6 +338,9 @@ var MemSearchInputSchema = {
|
|
|
337
338
|
semantic: z5.boolean().default(false).describe(
|
|
338
339
|
"Use semantic similarity from the embeddings index (requires `haive embeddings index`)."
|
|
339
340
|
),
|
|
341
|
+
lexical_rank: z5.boolean().default(false).describe(
|
|
342
|
+
"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."
|
|
343
|
+
),
|
|
340
344
|
min_score: z5.number().min(0).max(1).default(0).describe("Minimum cosine similarity (semantic mode only)"),
|
|
341
345
|
track: z5.boolean().default(true).describe("Increment read_count on returned memories (used for passive validation)")
|
|
342
346
|
};
|
|
@@ -362,6 +366,27 @@ async function memSearch(input, ctx) {
|
|
|
362
366
|
notice: "Semantic search unavailable (embeddings index missing or @hiveai/embeddings not installed). Falling back to literal search."
|
|
363
367
|
};
|
|
364
368
|
}
|
|
369
|
+
} else if (input.lexical_rank && input.query.trim()) {
|
|
370
|
+
const { ranked, scores } = rankMemoriesLexical(
|
|
371
|
+
filtered,
|
|
372
|
+
input.query,
|
|
373
|
+
input.limit
|
|
374
|
+
);
|
|
375
|
+
if (ranked.length > 0) {
|
|
376
|
+
const snippetNeedle = pickSnippetNeedle(input.query);
|
|
377
|
+
result = {
|
|
378
|
+
matches: ranked.map(
|
|
379
|
+
(loaded, i) => lexicalHit(loaded, snippetNeedle, usage, scores[i])
|
|
380
|
+
),
|
|
381
|
+
total: ranked.length,
|
|
382
|
+
mode: "lexical_ranked"
|
|
383
|
+
};
|
|
384
|
+
} else {
|
|
385
|
+
result = {
|
|
386
|
+
...buildLiteralResult(input, filtered, usage),
|
|
387
|
+
notice: "Lexical ranking found no BM25-positive hits \u2014 showing literal matches instead."
|
|
388
|
+
};
|
|
389
|
+
}
|
|
365
390
|
} else {
|
|
366
391
|
result = buildLiteralResult(input, filtered, usage);
|
|
367
392
|
}
|
|
@@ -459,6 +484,9 @@ function toHit(loaded, needle, usage) {
|
|
|
459
484
|
file_path: loaded.filePath
|
|
460
485
|
};
|
|
461
486
|
}
|
|
487
|
+
function lexicalHit(loaded, needle, usage, lexicalScore) {
|
|
488
|
+
return { ...toHit(loaded, needle, usage), score: lexicalScore };
|
|
489
|
+
}
|
|
462
490
|
|
|
463
491
|
// src/tools/mem-verify.ts
|
|
464
492
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
@@ -1099,7 +1127,11 @@ import {
|
|
|
1099
1127
|
import { z as z16 } from "zod";
|
|
1100
1128
|
|
|
1101
1129
|
// src/session-tracker.ts
|
|
1102
|
-
import {
|
|
1130
|
+
import {
|
|
1131
|
+
appendUsageEvent,
|
|
1132
|
+
appendRuntimeJournalEntry,
|
|
1133
|
+
loadConfig as loadConfig2
|
|
1134
|
+
} from "@hiveai/core";
|
|
1103
1135
|
import { mkdir as mkdir5, writeFile as writeFile9, rm } from "fs/promises";
|
|
1104
1136
|
import { existsSync as existsSync16 } from "fs";
|
|
1105
1137
|
import path7 from "path";
|
|
@@ -1172,6 +1204,14 @@ var SessionTracker = class {
|
|
|
1172
1204
|
recapId = result.id;
|
|
1173
1205
|
} catch {
|
|
1174
1206
|
}
|
|
1207
|
+
void appendRuntimeJournalEntry(this.ctx.paths, {
|
|
1208
|
+
kind: "session_end",
|
|
1209
|
+
message: recapId ? `auto session close | ${toolSummary} | recap:${recapId}` : `auto session close | ${toolSummary}`,
|
|
1210
|
+
meta: {
|
|
1211
|
+
recap_id: recapId ?? null,
|
|
1212
|
+
total_tool_calls: totalCalls
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1175
1215
|
const ranPostTask = this.events.some(
|
|
1176
1216
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
1177
1217
|
);
|
|
@@ -3040,13 +3080,139 @@ function gitFileDiff(root, file, sinceDays) {
|
|
|
3040
3080
|
}
|
|
3041
3081
|
}
|
|
3042
3082
|
|
|
3043
|
-
// src/
|
|
3083
|
+
// src/tools/mem-conflict-candidates.ts
|
|
3084
|
+
import { existsSync as existsSync27 } from "fs";
|
|
3085
|
+
import {
|
|
3086
|
+
findLexicalConflictPairs,
|
|
3087
|
+
findTopicStatusConflictPairs,
|
|
3088
|
+
loadMemoriesFromDir as loadMemoriesFromDir21
|
|
3089
|
+
} from "@hiveai/core";
|
|
3044
3090
|
import { z as z30 } from "zod";
|
|
3091
|
+
var MemConflictCandidatesInputSchema = {
|
|
3092
|
+
since_days: z30.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
3093
|
+
types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
3094
|
+
min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
3095
|
+
max_pairs: z30.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
3096
|
+
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."),
|
|
3097
|
+
max_topic_pairs: z30.number().int().positive().max(100).default(20).describe(
|
|
3098
|
+
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
3099
|
+
)
|
|
3100
|
+
};
|
|
3101
|
+
async function memConflictCandidates(input, ctx) {
|
|
3102
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
3103
|
+
return {
|
|
3104
|
+
pairs: [],
|
|
3105
|
+
topic_status_pairs: [],
|
|
3106
|
+
scanned: 0,
|
|
3107
|
+
truncated: false,
|
|
3108
|
+
notice: "No .ai/memories directory."
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
3112
|
+
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
3113
|
+
sinceDays: input.since_days,
|
|
3114
|
+
types: input.types,
|
|
3115
|
+
minJaccard: input.min_jaccard,
|
|
3116
|
+
maxPairs: input.max_pairs,
|
|
3117
|
+
maxScan: input.max_scan
|
|
3118
|
+
});
|
|
3119
|
+
const topicStatusPairs = findTopicStatusConflictPairs(all, input.max_topic_pairs);
|
|
3120
|
+
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;
|
|
3121
|
+
return { pairs, topic_status_pairs: topicStatusPairs, scanned, truncated, notice };
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// src/tools/mem-resolve-project.ts
|
|
3125
|
+
import { resolveProjectInfo } from "@hiveai/core";
|
|
3126
|
+
import { z as z31 } from "zod";
|
|
3127
|
+
var MemResolveProjectInputSchema = {
|
|
3128
|
+
cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
3129
|
+
};
|
|
3130
|
+
async function memResolveProject(input, _ctx) {
|
|
3131
|
+
void _ctx;
|
|
3132
|
+
return {
|
|
3133
|
+
ok: true,
|
|
3134
|
+
info: resolveProjectInfo({
|
|
3135
|
+
cwd: input.cwd
|
|
3136
|
+
})
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
// src/tools/mem-suggest-topic.ts
|
|
3141
|
+
import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
|
|
3142
|
+
import { z as z32 } from "zod";
|
|
3143
|
+
var MemSuggestTopicInputSchema = {
|
|
3144
|
+
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
3145
|
+
title: z32.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
3146
|
+
};
|
|
3147
|
+
async function memSuggestTopic(input, _ctx) {
|
|
3148
|
+
void _ctx;
|
|
3149
|
+
const suggestion = suggestTopicKey(input.type, input.title);
|
|
3150
|
+
return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
// src/tools/mem-timeline.ts
|
|
3154
|
+
import { existsSync as existsSync28 } from "fs";
|
|
3155
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hiveai/core";
|
|
3156
|
+
import { z as z33 } from "zod";
|
|
3157
|
+
var MemTimelineInputSchema = {
|
|
3158
|
+
memory_id: z33.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
3159
|
+
topic: z33.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
3160
|
+
limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3161
|
+
};
|
|
3162
|
+
async function memTimeline(input, ctx) {
|
|
3163
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
3164
|
+
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3165
|
+
}
|
|
3166
|
+
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
3167
|
+
const { entries, notice } = collectTimelineEntries(all, {
|
|
3168
|
+
memoryId: input.memory_id,
|
|
3169
|
+
topic: input.topic,
|
|
3170
|
+
limit: input.limit
|
|
3171
|
+
});
|
|
3172
|
+
return { entries, total: entries.length, notice };
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
// src/tools/runtime-journal-append.ts
|
|
3176
|
+
import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
|
|
3177
|
+
import { z as z34 } from "zod";
|
|
3178
|
+
var RuntimeJournalAppendInputSchema = {
|
|
3179
|
+
message: z34.string().min(1).describe("Short line to append to the runtime session journal"),
|
|
3180
|
+
kind: z34.enum(["note", "session_end", "mcp"]).default("note"),
|
|
3181
|
+
tool: z34.string().optional().describe("When kind=mcp, which tool name (optional)")
|
|
3182
|
+
};
|
|
3183
|
+
async function runtimeJournalAppend(input, ctx) {
|
|
3184
|
+
await appendRuntimeJournalEntry2(ctx.paths, {
|
|
3185
|
+
kind: input.kind,
|
|
3186
|
+
message: input.message,
|
|
3187
|
+
...input.tool ? { tool: input.tool } : {}
|
|
3188
|
+
});
|
|
3189
|
+
return {
|
|
3190
|
+
ok: true,
|
|
3191
|
+
path_hint: `${ctx.paths.runtimeDir}/session-journal.ndjson`
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
// src/tools/runtime-journal-tail.ts
|
|
3196
|
+
import { readRuntimeJournalTail } from "@hiveai/core";
|
|
3197
|
+
import { z as z35 } from "zod";
|
|
3198
|
+
var RuntimeJournalTailInputSchema = {
|
|
3199
|
+
limit: z35.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
|
|
3200
|
+
};
|
|
3201
|
+
async function runtimeJournalTail(input, ctx) {
|
|
3202
|
+
const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
|
|
3203
|
+
if (entries.length === 0) {
|
|
3204
|
+
return { entries: [], empty: true };
|
|
3205
|
+
}
|
|
3206
|
+
return { entries };
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
// src/prompts/bootstrap-project.ts
|
|
3210
|
+
import { z as z36 } from "zod";
|
|
3045
3211
|
var BootstrapProjectArgsSchema = {
|
|
3046
|
-
module:
|
|
3212
|
+
module: z36.string().optional().describe(
|
|
3047
3213
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
3048
3214
|
),
|
|
3049
|
-
focus:
|
|
3215
|
+
focus: z36.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
3050
3216
|
};
|
|
3051
3217
|
var ROOT_TEMPLATE = `# Project context
|
|
3052
3218
|
|
|
@@ -3128,10 +3294,10 @@ ${template}\`\`\`
|
|
|
3128
3294
|
}
|
|
3129
3295
|
|
|
3130
3296
|
// src/prompts/post-task.ts
|
|
3131
|
-
import { z as
|
|
3297
|
+
import { z as z37 } from "zod";
|
|
3132
3298
|
var PostTaskArgsSchema = {
|
|
3133
|
-
task_summary:
|
|
3134
|
-
files_touched:
|
|
3299
|
+
task_summary: z37.string().optional().describe("One sentence describing what you just did"),
|
|
3300
|
+
files_touched: z37.array(z37.string()).optional().describe("Files you created or modified during the task")
|
|
3135
3301
|
};
|
|
3136
3302
|
function postTaskPrompt(args, ctx) {
|
|
3137
3303
|
const taskLine = args.task_summary ? `
|
|
@@ -3215,12 +3381,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
3215
3381
|
}
|
|
3216
3382
|
|
|
3217
3383
|
// src/prompts/import-docs.ts
|
|
3218
|
-
import { z as
|
|
3384
|
+
import { z as z38 } from "zod";
|
|
3219
3385
|
var ImportDocsArgsSchema = {
|
|
3220
|
-
content:
|
|
3221
|
-
source:
|
|
3222
|
-
scope:
|
|
3223
|
-
dry_run:
|
|
3386
|
+
content: z38.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
3387
|
+
source: z38.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
3388
|
+
scope: z38.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
3389
|
+
dry_run: z38.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
3224
3390
|
};
|
|
3225
3391
|
function importDocsPrompt(args, ctx) {
|
|
3226
3392
|
const sourceLine = args.source ? `
|
|
@@ -3285,7 +3451,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3285
3451
|
|
|
3286
3452
|
// src/server.ts
|
|
3287
3453
|
var SERVER_NAME = "haive";
|
|
3288
|
-
var SERVER_VERSION = "0.9.
|
|
3454
|
+
var SERVER_VERSION = "0.9.6";
|
|
3289
3455
|
function jsonResult(data) {
|
|
3290
3456
|
return {
|
|
3291
3457
|
content: [
|
|
@@ -3336,6 +3502,23 @@ function createHaiveServer(options = {}) {
|
|
|
3336
3502
|
return jsonResult(await memSave(input, context));
|
|
3337
3503
|
}
|
|
3338
3504
|
);
|
|
3505
|
+
server.tool(
|
|
3506
|
+
"mem_suggest_topic",
|
|
3507
|
+
[
|
|
3508
|
+
"Propose a stable `topic` key (topic-upsert) from type + short title.",
|
|
3509
|
+
"",
|
|
3510
|
+
"USE BEFORE mem_save when you want deterministic updates to the same memory over time;",
|
|
3511
|
+
"families mimic Engram-style grouping (architecture/*, bug/*, decision/*, \u2026).",
|
|
3512
|
+
"",
|
|
3513
|
+
"PARAMETERS:",
|
|
3514
|
+
" type \u2014 convention | decision | gotcha | architecture | glossary | attempt | session_recap",
|
|
3515
|
+
" title \u2014 phrase to slugify under the suggested family prefix",
|
|
3516
|
+
"",
|
|
3517
|
+
"RETURNS: { topic_key, family, type }"
|
|
3518
|
+
].join("\n"),
|
|
3519
|
+
MemSuggestTopicInputSchema,
|
|
3520
|
+
async (input) => jsonResult(await memSuggestTopic(input, context))
|
|
3521
|
+
);
|
|
3339
3522
|
server.tool(
|
|
3340
3523
|
"mem_tried",
|
|
3341
3524
|
[
|
|
@@ -3427,8 +3610,12 @@ function createHaiveServer(options = {}) {
|
|
|
3427
3610
|
server.tool(
|
|
3428
3611
|
"get_briefing",
|
|
3429
3612
|
[
|
|
3430
|
-
"\u2B50
|
|
3431
|
-
"
|
|
3613
|
+
"\u2B50 DEFAULT-FIRST for coding agents on any repo where `haive init` ran: call this BEFORE",
|
|
3614
|
+
"changing source or project config for the current goal (unless the developer explicitly opts out).",
|
|
3615
|
+
"One-shot onboarding: everything relevant in a single call under a token budget.",
|
|
3616
|
+
"",
|
|
3617
|
+
"PROGRESSIVE DISCLOSURE \u2014 after this, drill down only if needed:",
|
|
3618
|
+
" mem_relevant_to / mem_search (compact lists) \u2192 mem_get (full body + anchors).",
|
|
3432
3619
|
"",
|
|
3433
3620
|
"RETURNS (in order of priority):",
|
|
3434
3621
|
" 0. action_required \u2014 \u26A0\uFE0F HANDLE THIS FIRST if non-empty (see protocol below)",
|
|
@@ -3463,7 +3650,7 @@ function createHaiveServer(options = {}) {
|
|
|
3463
3650
|
" low \u2014 proposed, few reads (take with caution)",
|
|
3464
3651
|
" unverified \u2014 draft (unverified: true flag set)",
|
|
3465
3652
|
"",
|
|
3466
|
-
"Replaces 4\u20135 separate tool calls.
|
|
3653
|
+
"Replaces 4\u20135 separate tool calls. Prefer this first; use mem_search / mem_get only for follow-up."
|
|
3467
3654
|
].join("\n"),
|
|
3468
3655
|
GetBriefingInputSchema,
|
|
3469
3656
|
async (input) => {
|
|
@@ -3482,6 +3669,8 @@ function createHaiveServer(options = {}) {
|
|
|
3482
3669
|
"SEARCH MODES:",
|
|
3483
3670
|
" Literal (default): AND search across id, tags, and body \u2014 all tokens must match.",
|
|
3484
3671
|
" Falls back to OR automatically if no AND results (partial match).",
|
|
3672
|
+
" Lexical rank (lexical_rank: true, semantic: false): Okapi-BM25-style scoring on the",
|
|
3673
|
+
" filtered corpus \u2014 good for phrase-like queries without embeddings.",
|
|
3485
3674
|
" Semantic (semantic: true): embedding-based similarity \u2014 finds related memories",
|
|
3486
3675
|
" even with different wording. Requires haive embeddings index to be built.",
|
|
3487
3676
|
"",
|
|
@@ -3490,6 +3679,7 @@ function createHaiveServer(options = {}) {
|
|
|
3490
3679
|
" scope \u2014 filter by personal | team | module",
|
|
3491
3680
|
" type \u2014 filter by convention | decision | gotcha | architecture | glossary",
|
|
3492
3681
|
" semantic \u2014 true for embedding-based search (requires @hiveai/embeddings)",
|
|
3682
|
+
" lexical_rank \u2014 BM25-style ranking (ignored when semantic is true)",
|
|
3493
3683
|
" limit \u2014 max results (default 10)",
|
|
3494
3684
|
"",
|
|
3495
3685
|
"RETURNS: array of { id, type, scope, status, confidence, body, match_quality }"
|
|
@@ -3500,6 +3690,22 @@ function createHaiveServer(options = {}) {
|
|
|
3500
3690
|
return jsonResult(await memSearch(input, context));
|
|
3501
3691
|
}
|
|
3502
3692
|
);
|
|
3693
|
+
server.tool(
|
|
3694
|
+
"mem_timeline",
|
|
3695
|
+
[
|
|
3696
|
+
"Chronological view of related memories: by shared frontmatter.topic OR expanded from a seed id",
|
|
3697
|
+
"(related_ids, same topic, overlapping anchor paths \u2014 one extra hop on related_ids).",
|
|
3698
|
+
"",
|
|
3699
|
+
"PARAMETERS:",
|
|
3700
|
+
" memory_id \u2014 optional seed memory id",
|
|
3701
|
+
" topic \u2014 optional topic key (required if memory_id omitted)",
|
|
3702
|
+
" limit \u2014 max entries (default 30)",
|
|
3703
|
+
"",
|
|
3704
|
+
"RETURNS: { entries: [{ id, type, scope, created_at, one_line, topic? }], total, notice? }"
|
|
3705
|
+
].join("\n"),
|
|
3706
|
+
MemTimelineInputSchema,
|
|
3707
|
+
async (input) => jsonResult(await memTimeline(input, context))
|
|
3708
|
+
);
|
|
3503
3709
|
server.tool(
|
|
3504
3710
|
"mem_for_files",
|
|
3505
3711
|
[
|
|
@@ -3529,7 +3735,7 @@ function createHaiveServer(options = {}) {
|
|
|
3529
3735
|
[
|
|
3530
3736
|
"Fetch a single memory by its full id with all details.",
|
|
3531
3737
|
"",
|
|
3532
|
-
"USE WHEN get_briefing returned a
|
|
3738
|
+
"USE WHEN get_briefing / mem_relevant_to / mem_search returned a compact hit and you need",
|
|
3533
3739
|
"the full body, or when you know the exact id of a memory.",
|
|
3534
3740
|
"",
|
|
3535
3741
|
"PARAMETERS:",
|
|
@@ -3617,6 +3823,22 @@ function createHaiveServer(options = {}) {
|
|
|
3617
3823
|
CodeMapInputSchema,
|
|
3618
3824
|
async (input) => jsonResult(await codeMapTool(input, context))
|
|
3619
3825
|
);
|
|
3826
|
+
server.tool(
|
|
3827
|
+
"mem_resolve_project",
|
|
3828
|
+
[
|
|
3829
|
+
"Diagnostics: resolve which project root hAIve is using (never throws).",
|
|
3830
|
+
"",
|
|
3831
|
+
"USE IN multi-root workspaces or when the agent CWD may not be the repo root \u2014",
|
|
3832
|
+
"mirrors HAIVE_PROJECT_ROOT, findProjectRoot markers, and presence of .ai/memories.",
|
|
3833
|
+
"",
|
|
3834
|
+
"PARAMETERS:",
|
|
3835
|
+
" cwd \u2014 optional directory used for discovery when HAIVE_PROJECT_ROOT is unset",
|
|
3836
|
+
"",
|
|
3837
|
+
"RETURNS: { ok: true, info: { cwd, resolved_root, haive_project_root_env, \u2026 } }"
|
|
3838
|
+
].join("\n"),
|
|
3839
|
+
MemResolveProjectInputSchema,
|
|
3840
|
+
async (input) => jsonResult(await memResolveProject(input, context))
|
|
3841
|
+
);
|
|
3620
3842
|
server.tool(
|
|
3621
3843
|
"mem_update",
|
|
3622
3844
|
[
|
|
@@ -3750,6 +3972,8 @@ function createHaiveServer(options = {}) {
|
|
|
3750
3972
|
"One-shot ranked memories for a task \u2014 use instead of get_briefing when",
|
|
3751
3973
|
"project context is already loaded and you only want the relevant memory layer.",
|
|
3752
3974
|
"",
|
|
3975
|
+
"Second step in progressive disclosure (after get_briefing): narrow here, then mem_get for full text.",
|
|
3976
|
+
"",
|
|
3753
3977
|
"Reuses the same ranking pipeline (anchor / module / literal / semantic) but",
|
|
3754
3978
|
"skips project_context, modules, action_required, etc.",
|
|
3755
3979
|
"",
|
|
@@ -3907,6 +4131,53 @@ function createHaiveServer(options = {}) {
|
|
|
3907
4131
|
return jsonResult(await memConflicts(input, context));
|
|
3908
4132
|
}
|
|
3909
4133
|
);
|
|
4134
|
+
server.tool(
|
|
4135
|
+
"mem_conflict_candidates",
|
|
4136
|
+
[
|
|
4137
|
+
"Bulk scan for conflict CANDIDATES (not proof):",
|
|
4138
|
+
"",
|
|
4139
|
+
" 1. Lexical similarity (Jaccard) on decision/architecture-like pairs",
|
|
4140
|
+
" 2. Same frontmatter.topic with validated vs rejected \u2014 quick human-review signal",
|
|
4141
|
+
"",
|
|
4142
|
+
"Advisory only \u2014 follow with mem_conflicts_with on specific ids.",
|
|
4143
|
+
"",
|
|
4144
|
+
"PARAMETERS:",
|
|
4145
|
+
" since_days, types, min_jaccard, max_pairs, max_scan, max_topic_pairs",
|
|
4146
|
+
"",
|
|
4147
|
+
"RETURNS: { pairs, topic_status_pairs, scanned, truncated, notice? }"
|
|
4148
|
+
].join("\n"),
|
|
4149
|
+
MemConflictCandidatesInputSchema,
|
|
4150
|
+
async (input) => {
|
|
4151
|
+
tracker.record("mem_conflict_candidates", `${input.since_days}d`);
|
|
4152
|
+
return jsonResult(await memConflictCandidates(input, context));
|
|
4153
|
+
}
|
|
4154
|
+
);
|
|
4155
|
+
server.tool(
|
|
4156
|
+
"runtime_journal_append",
|
|
4157
|
+
[
|
|
4158
|
+
"Append one line to `.ai/.runtime/session-journal.ndjson` \u2014 machine-local session continuity.",
|
|
4159
|
+
"",
|
|
4160
|
+
"Does NOT replace team memories; complements mem_session_end recaps for local traces.",
|
|
4161
|
+
"",
|
|
4162
|
+
"PARAMETERS: message, kind (note|session_end|mcp), optional tool",
|
|
4163
|
+
"",
|
|
4164
|
+
"RETURNS: { ok, path_hint }"
|
|
4165
|
+
].join("\n"),
|
|
4166
|
+
RuntimeJournalAppendInputSchema,
|
|
4167
|
+
async (input) => jsonResult(await runtimeJournalAppend(input, context))
|
|
4168
|
+
);
|
|
4169
|
+
server.tool(
|
|
4170
|
+
"runtime_journal_tail",
|
|
4171
|
+
[
|
|
4172
|
+
"Read the last N entries from the runtime session journal (parsed JSON lines).",
|
|
4173
|
+
"",
|
|
4174
|
+
"PARAMETERS: limit (default 30, max 500)",
|
|
4175
|
+
"",
|
|
4176
|
+
"RETURNS: { entries: [...], empty?: true }"
|
|
4177
|
+
].join("\n"),
|
|
4178
|
+
RuntimeJournalTailInputSchema,
|
|
4179
|
+
async (input) => jsonResult(await runtimeJournalTail(input, context))
|
|
4180
|
+
);
|
|
3910
4181
|
server.tool(
|
|
3911
4182
|
"pre_commit_check",
|
|
3912
4183
|
[
|