@hiveai/mcp 0.9.15 → 0.9.17
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 +12 -3
- package/dist/index.js +201 -16
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +19 -2
- package/dist/server.js +206 -16
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **hAIve MCP server** — policy-aware briefing and memory tools for MCP-compatible AI coding agents.
|
|
4
4
|
|
|
5
|
-
The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks.
|
|
5
|
+
The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks. Larger maintenance and experimental surfaces are opt-in via `HAIVE_TOOL_PROFILE`.
|
|
6
6
|
|
|
7
7
|
hAIve is not just a memory database. The MCP layer participates in enforcement: state-changing hAIve tools require `get_briefing` or `mem_relevant_to` first, so agents cannot silently skip team context while using hAIve.
|
|
8
8
|
|
|
@@ -117,17 +117,26 @@ Default tools:
|
|
|
117
117
|
- `mem_tried`
|
|
118
118
|
- `mem_search`
|
|
119
119
|
- `mem_get`
|
|
120
|
-
- `mem_update`
|
|
121
120
|
- `mem_verify`
|
|
122
121
|
- `code_map`
|
|
123
122
|
- `pre_commit_check`
|
|
123
|
+
- `mem_session_end`
|
|
124
124
|
|
|
125
125
|
Default prompts:
|
|
126
126
|
|
|
127
127
|
- `bootstrap_project`
|
|
128
128
|
- `post_task`
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
### Tool Profiles
|
|
131
|
+
|
|
132
|
+
`HAIVE_TOOL_PROFILE` controls how much hAIve surface an agent sees:
|
|
133
|
+
|
|
134
|
+
- `enforcement` (default): compact repo-native context harness for coding agents.
|
|
135
|
+
- `maintenance`: adds corpus review, lifecycle, distillation, code-search, and project-context maintenance tools.
|
|
136
|
+
- `experimental`: adds exploratory diagnostics such as runtime journal, pattern detection, why-this-file, why-this-decision, and conflict analysis.
|
|
137
|
+
- `full`: legacy alias for `experimental`.
|
|
138
|
+
|
|
139
|
+
Use `maintenance` for human/team stewardship sessions and `experimental` only when you are intentionally working on hAIve's broader research tooling.
|
|
131
140
|
|
|
132
141
|
### `get_briefing` ⭐ Start every task with this
|
|
133
142
|
|
package/dist/index.js
CHANGED
|
@@ -224,7 +224,6 @@ async function memSave(input, ctx) {
|
|
|
224
224
|
const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
|
|
225
225
|
const newFrontmatter = {
|
|
226
226
|
...fm,
|
|
227
|
-
body: input.body,
|
|
228
227
|
tags: input.tags.length ? input.tags : fm.tags,
|
|
229
228
|
revision_count: (fm.revision_count ?? 0) + 1,
|
|
230
229
|
anchor: {
|
|
@@ -240,6 +239,7 @@ async function memSave(input, ctx) {
|
|
|
240
239
|
);
|
|
241
240
|
const mergedTw = [
|
|
242
241
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
|
|
242
|
+
criticalAnchorWarning(input.type, fm.status, newFrontmatter.anchor.paths, newFrontmatter.anchor.symbols),
|
|
243
243
|
simW ?? null
|
|
244
244
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
245
245
|
return {
|
|
@@ -295,6 +295,7 @@ async function memSave(input, ctx) {
|
|
|
295
295
|
const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
|
|
296
296
|
const finalWarning = [
|
|
297
297
|
invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
|
|
298
|
+
criticalAnchorWarning(frontmatter.type, frontmatter.status, frontmatter.anchor.paths, frontmatter.anchor.symbols),
|
|
298
299
|
warning ?? null,
|
|
299
300
|
simWarnNew ?? null
|
|
300
301
|
].filter(Boolean).join(" \u2014 ") || void 0;
|
|
@@ -309,6 +310,12 @@ async function memSave(input, ctx) {
|
|
|
309
310
|
...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
|
|
310
311
|
};
|
|
311
312
|
}
|
|
313
|
+
function criticalAnchorWarning(type, status, paths, symbols) {
|
|
314
|
+
if (!["decision", "gotcha", "architecture"].includes(type)) return null;
|
|
315
|
+
if (status !== "validated") return null;
|
|
316
|
+
if (paths.length > 0 || symbols.length > 0) return null;
|
|
317
|
+
return `${type} is validated without paths or symbols; add anchors so hAIve can detect drift.`;
|
|
318
|
+
}
|
|
312
319
|
|
|
313
320
|
// src/tools/mem-search.ts
|
|
314
321
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -1385,6 +1392,7 @@ import {
|
|
|
1385
1392
|
extractActionsBriefBody,
|
|
1386
1393
|
getUsage as getUsage5,
|
|
1387
1394
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1395
|
+
isGlobPath,
|
|
1388
1396
|
isAutoPromoteEligible,
|
|
1389
1397
|
isDecaying,
|
|
1390
1398
|
literalMatchesAllTokens as literalMatchesAllTokens2,
|
|
@@ -1506,6 +1514,7 @@ async function getBriefing(input, ctx) {
|
|
|
1506
1514
|
reasons: [reason],
|
|
1507
1515
|
match_quality: matchQuality ?? "partial",
|
|
1508
1516
|
...score !== void 0 ? { semantic_score: score } : {},
|
|
1517
|
+
priority: "background",
|
|
1509
1518
|
body: loaded.memory.body,
|
|
1510
1519
|
file_path: loaded.filePath
|
|
1511
1520
|
});
|
|
@@ -1521,6 +1530,13 @@ async function getBriefing(input, ctx) {
|
|
|
1521
1530
|
if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module", void 0, "partial");
|
|
1522
1531
|
}
|
|
1523
1532
|
}
|
|
1533
|
+
if (input.symbols.length > 0) {
|
|
1534
|
+
const wanted = new Set(input.symbols.map((s) => s.toLowerCase()));
|
|
1535
|
+
for (const loaded of allMemories) {
|
|
1536
|
+
const symbols = loaded.memory.frontmatter.anchor.symbols.map((s) => s.toLowerCase());
|
|
1537
|
+
if (symbols.some((s) => wanted.has(s))) addOrUpdate(loaded, "symbol", void 0, "exact");
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1524
1540
|
if (input.task) {
|
|
1525
1541
|
const tokens = tokenizeQuery2(input.task);
|
|
1526
1542
|
const andHits = allMemories.filter((m) => literalMatchesAllTokens2(m.memory, tokens));
|
|
@@ -1546,11 +1562,12 @@ async function getBriefing(input, ctx) {
|
|
|
1546
1562
|
}
|
|
1547
1563
|
}
|
|
1548
1564
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
1565
|
+
const priorityScore = (m) => priorityRank(classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols));
|
|
1549
1566
|
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
|
|
1550
|
-
(m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
1567
|
+
(m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
1551
1568
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
1552
|
-
const sa = reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
1553
|
-
const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1569
|
+
const sa = priorityScore(a) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
|
|
1570
|
+
const sb = priorityScore(b) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
|
|
1554
1571
|
return sb - sa;
|
|
1555
1572
|
});
|
|
1556
1573
|
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
@@ -1709,8 +1726,15 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1709
1726
|
})) : trimmedMemories;
|
|
1710
1727
|
const outputMemories = formattedMemories.map((m) => ({
|
|
1711
1728
|
...m,
|
|
1729
|
+
priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
|
|
1712
1730
|
why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
|
|
1713
1731
|
}));
|
|
1732
|
+
const briefingQuality = classifyBriefingQuality(outputMemories, {
|
|
1733
|
+
isTemplateContext,
|
|
1734
|
+
autoContextGenerated,
|
|
1735
|
+
hasLastSession: Boolean(lastSession),
|
|
1736
|
+
searchMode
|
|
1737
|
+
});
|
|
1714
1738
|
let symbolLocations;
|
|
1715
1739
|
const symbolsToLookup = new Set(input.symbols);
|
|
1716
1740
|
for (const m of outputMemories) {
|
|
@@ -1851,6 +1875,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1851
1875
|
} : null,
|
|
1852
1876
|
module_contexts: trimmedModules,
|
|
1853
1877
|
memories: outputMemories,
|
|
1878
|
+
briefing_quality: briefingQuality,
|
|
1854
1879
|
...symbolLocations ? { symbol_locations: symbolLocations } : {},
|
|
1855
1880
|
action_required: actionRequired,
|
|
1856
1881
|
decay_warnings: decayWarnings,
|
|
@@ -1876,6 +1901,53 @@ function compactSummary(body) {
|
|
|
1876
1901
|
}
|
|
1877
1902
|
return body.slice(0, 120);
|
|
1878
1903
|
}
|
|
1904
|
+
function classifyMemoryPriority(memory, loaded, inputFiles, inputSymbols) {
|
|
1905
|
+
const fm = loaded?.memory.frontmatter;
|
|
1906
|
+
const directAnchor = Boolean(
|
|
1907
|
+
fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
|
|
1908
|
+
);
|
|
1909
|
+
const directSymbol = Boolean(
|
|
1910
|
+
fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
|
|
1911
|
+
(sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
|
|
1912
|
+
)
|
|
1913
|
+
);
|
|
1914
|
+
const strongSemantic = (memory.semantic_score ?? 0) >= 0.65;
|
|
1915
|
+
const usefulSemantic = (memory.semantic_score ?? 0) >= 0.35;
|
|
1916
|
+
if (fm?.requires_human_approval || directAnchor || directSymbol || memory.type === "attempt" && (memory.match_quality === "exact" || strongSemantic)) {
|
|
1917
|
+
return "must_read";
|
|
1918
|
+
}
|
|
1919
|
+
if (memory.reasons.includes("module") || memory.reasons.includes("domain") || memory.match_quality === "exact" || usefulSemantic) {
|
|
1920
|
+
return "useful";
|
|
1921
|
+
}
|
|
1922
|
+
return "background";
|
|
1923
|
+
}
|
|
1924
|
+
function priorityRank(priority) {
|
|
1925
|
+
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
1926
|
+
}
|
|
1927
|
+
function classifyBriefingQuality(memories, context) {
|
|
1928
|
+
const mustRead = memories.filter((m) => m.priority === "must_read").length;
|
|
1929
|
+
const useful = memories.filter((m) => m.priority === "useful").length;
|
|
1930
|
+
const background = memories.filter((m) => m.priority === "background").length;
|
|
1931
|
+
const weakSemantic = memories.filter(
|
|
1932
|
+
(m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
|
|
1933
|
+
).length;
|
|
1934
|
+
const reasons = [];
|
|
1935
|
+
if (memories.length === 0) reasons.push("no memories matched the task or files");
|
|
1936
|
+
if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
|
|
1937
|
+
if (!context.hasLastSession) reasons.push("no previous session recap");
|
|
1938
|
+
if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
|
|
1939
|
+
if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
|
|
1940
|
+
if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
|
|
1941
|
+
if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
|
|
1942
|
+
if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
|
|
1943
|
+
if (memories.length === 0 || mustRead === 0 && useful === 0) {
|
|
1944
|
+
return { level: "thin", reasons };
|
|
1945
|
+
}
|
|
1946
|
+
if (background > useful + mustRead && background > 2) {
|
|
1947
|
+
return { level: "noisy", reasons };
|
|
1948
|
+
}
|
|
1949
|
+
return { level: "strong", reasons };
|
|
1950
|
+
}
|
|
1879
1951
|
function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
|
|
1880
1952
|
const why = [];
|
|
1881
1953
|
const fm = loaded?.memory.frontmatter;
|
|
@@ -1884,7 +1956,19 @@ function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
|
|
|
1884
1956
|
(p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
|
|
1885
1957
|
);
|
|
1886
1958
|
if (matching.length > 0) {
|
|
1887
|
-
|
|
1959
|
+
const exact = matching.filter(
|
|
1960
|
+
(p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
|
|
1961
|
+
);
|
|
1962
|
+
const glob = matching.filter((p) => isGlobPath(p));
|
|
1963
|
+
if (exact.length > 0) {
|
|
1964
|
+
why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
|
|
1965
|
+
}
|
|
1966
|
+
if (glob.length > 0) {
|
|
1967
|
+
why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
|
|
1968
|
+
}
|
|
1969
|
+
if (exact.length === 0 && glob.length === 0) {
|
|
1970
|
+
why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
|
|
1971
|
+
}
|
|
1888
1972
|
} else if (fm.anchor.paths.length > 0) {
|
|
1889
1973
|
why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
|
|
1890
1974
|
}
|
|
@@ -1892,6 +1976,9 @@ function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
|
|
|
1892
1976
|
why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
1893
1977
|
}
|
|
1894
1978
|
}
|
|
1979
|
+
if (memory.reasons.includes("symbol") && fm) {
|
|
1980
|
+
why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
|
|
1981
|
+
}
|
|
1895
1982
|
if (memory.reasons.includes("module")) {
|
|
1896
1983
|
const moduleHints = [
|
|
1897
1984
|
...memory.module ? [memory.module] : [],
|
|
@@ -2133,7 +2220,8 @@ async function memRelevantTo(input, ctx) {
|
|
|
2133
2220
|
const out = {
|
|
2134
2221
|
task: input.task,
|
|
2135
2222
|
search_mode: briefing.search_mode,
|
|
2136
|
-
memories: briefing.memories
|
|
2223
|
+
memories: briefing.memories,
|
|
2224
|
+
briefing_quality: briefing.briefing_quality
|
|
2137
2225
|
};
|
|
2138
2226
|
if (briefing.hints && briefing.hints.length > 0) out.hints = briefing.hints;
|
|
2139
2227
|
if (briefing.memories.length === 0) out.empty = true;
|
|
@@ -2880,7 +2968,7 @@ async function preCommitCheck(input, ctx) {
|
|
|
2880
2968
|
const filesTouching = new Set(relevantMatches.map((m) => m.id));
|
|
2881
2969
|
const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
|
|
2882
2970
|
const blockOn = input.block_on;
|
|
2883
|
-
const classifiedWarnings = apResult.warnings.map(classifyWarning);
|
|
2971
|
+
const classifiedWarnings = apResult.warnings.map((warning) => classifyWarning(warning, input.paths));
|
|
2884
2972
|
const blockingWarnings = classifiedWarnings.filter((w) => w.level === "blocking");
|
|
2885
2973
|
const reviewWarnings = classifiedWarnings.filter((w) => w.level === "review");
|
|
2886
2974
|
const infoWarnings = classifiedWarnings.filter((w) => w.level === "info");
|
|
@@ -2915,12 +3003,26 @@ async function preCommitCheck(input, ctx) {
|
|
|
2915
3003
|
}))
|
|
2916
3004
|
};
|
|
2917
3005
|
}
|
|
2918
|
-
function classifyWarning(warning) {
|
|
3006
|
+
function classifyWarning(warning, paths) {
|
|
3007
|
+
const affectedFiles = paths.filter((p) => !p.startsWith(".ai/.usage/"));
|
|
3008
|
+
const repairCommand = repairCommandForWarning(warning, affectedFiles);
|
|
3009
|
+
const fileDowngrade = fileTypeDowngradeReason(warning, affectedFiles);
|
|
3010
|
+
if (fileDowngrade) {
|
|
3011
|
+
return {
|
|
3012
|
+
...warning,
|
|
3013
|
+
level: "info",
|
|
3014
|
+
rationale: fileDowngrade,
|
|
3015
|
+
affected_files: affectedFiles,
|
|
3016
|
+
repair_command: repairCommand
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
2919
3019
|
if (isBlockingWarning(warning)) {
|
|
2920
3020
|
return {
|
|
2921
3021
|
...warning,
|
|
2922
3022
|
level: "blocking",
|
|
2923
|
-
rationale: "authoritative/trusted memory plus strong semantic match to the diff (score >= 0.65)"
|
|
3023
|
+
rationale: "authoritative/trusted memory plus strong semantic match to the diff (score >= 0.65)",
|
|
3024
|
+
affected_files: affectedFiles,
|
|
3025
|
+
repair_command: repairCommand
|
|
2924
3026
|
};
|
|
2925
3027
|
}
|
|
2926
3028
|
const hasSemantic = warning.reasons.includes("semantic");
|
|
@@ -2930,13 +3032,17 @@ function classifyWarning(warning) {
|
|
|
2930
3032
|
return {
|
|
2931
3033
|
...warning,
|
|
2932
3034
|
level: "review",
|
|
2933
|
-
rationale: hasSemantic ? "semantic match is plausible but below blocking threshold" : "anchored high-confidence memory also matched diff tokens, but no strong semantic proof"
|
|
3035
|
+
rationale: hasSemantic ? "semantic match is plausible but below blocking threshold" : "anchored high-confidence memory also matched diff tokens, but no strong semantic proof",
|
|
3036
|
+
affected_files: affectedFiles,
|
|
3037
|
+
repair_command: repairCommand
|
|
2934
3038
|
};
|
|
2935
3039
|
}
|
|
2936
3040
|
return {
|
|
2937
3041
|
...warning,
|
|
2938
3042
|
level: "info",
|
|
2939
|
-
rationale: "weak signal only (literal/anchor/low semantic evidence); surfaced for audit, hidden in concise CLI output"
|
|
3043
|
+
rationale: "weak signal only (literal/anchor/low semantic evidence); surfaced for audit, hidden in concise CLI output",
|
|
3044
|
+
affected_files: affectedFiles,
|
|
3045
|
+
repair_command: repairCommand
|
|
2940
3046
|
};
|
|
2941
3047
|
}
|
|
2942
3048
|
function isBlockingWarning(warning) {
|
|
@@ -2944,6 +3050,40 @@ function isBlockingWarning(warning) {
|
|
|
2944
3050
|
if (!highConfidence) return false;
|
|
2945
3051
|
return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
|
|
2946
3052
|
}
|
|
3053
|
+
function fileTypeDowngradeReason(warning, paths) {
|
|
3054
|
+
if (paths.length === 0) return null;
|
|
3055
|
+
if (paths.every((p) => p.startsWith(".ai/.usage/") || p === ".ai/.usage/tool-usage.jsonl")) {
|
|
3056
|
+
return ".ai usage logs are local telemetry and never block commits.";
|
|
3057
|
+
}
|
|
3058
|
+
const docsOnly = paths.every(isDocLikePath);
|
|
3059
|
+
if (docsOnly && !hasStrongSemantic(warning)) {
|
|
3060
|
+
return "docs/changelog-only change; anti-pattern is downgraded unless semantic evidence is strong.";
|
|
3061
|
+
}
|
|
3062
|
+
const configOnly = paths.every(isPackageOrConfigPath);
|
|
3063
|
+
if (configOnly && looksRuntimeSpecific(warning) && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
|
|
3064
|
+
return "package/config-only change; runtime-specific gotcha is not anchored or semantically strong.";
|
|
3065
|
+
}
|
|
3066
|
+
return null;
|
|
3067
|
+
}
|
|
3068
|
+
function hasStrongSemantic(warning) {
|
|
3069
|
+
return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
|
|
3070
|
+
}
|
|
3071
|
+
function isDocLikePath(file) {
|
|
3072
|
+
const lower = file.toLowerCase();
|
|
3073
|
+
return lower.endsWith(".md") || lower.includes("changelog") || lower.startsWith("docs/") || lower.startsWith(".github/") && lower.endsWith(".md");
|
|
3074
|
+
}
|
|
3075
|
+
function isPackageOrConfigPath(file) {
|
|
3076
|
+
const lower = file.toLowerCase();
|
|
3077
|
+
return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") || lower.endsWith(".json") || lower.endsWith(".yml") || lower.endsWith(".yaml") || lower.startsWith(".github/workflows/");
|
|
3078
|
+
}
|
|
3079
|
+
function looksRuntimeSpecific(warning) {
|
|
3080
|
+
const text = `${warning.body_preview} ${warning.id}`.toLowerCase();
|
|
3081
|
+
return /\b(runtime|controller|request|response|database|transaction|auth|cache|production|service|api|endpoint)\b/.test(text);
|
|
3082
|
+
}
|
|
3083
|
+
function repairCommandForWarning(warning, paths) {
|
|
3084
|
+
const firstPath = paths[0];
|
|
3085
|
+
return firstPath ? `haive briefing --files "${firstPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
|
|
3086
|
+
}
|
|
2947
3087
|
|
|
2948
3088
|
// src/tools/pattern-detect.ts
|
|
2949
3089
|
import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
|
|
@@ -3530,7 +3670,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3530
3670
|
// src/server.ts
|
|
3531
3671
|
import { loadConfigSync } from "@hiveai/core";
|
|
3532
3672
|
var SERVER_NAME = "haive";
|
|
3533
|
-
var SERVER_VERSION = "0.9.
|
|
3673
|
+
var SERVER_VERSION = "0.9.17";
|
|
3534
3674
|
function jsonResult(data) {
|
|
3535
3675
|
return {
|
|
3536
3676
|
content: [
|
|
@@ -3541,7 +3681,7 @@ function jsonResult(data) {
|
|
|
3541
3681
|
]
|
|
3542
3682
|
};
|
|
3543
3683
|
}
|
|
3544
|
-
var ENFORCEMENT_PROFILE_TOOLS =
|
|
3684
|
+
var ENFORCEMENT_PROFILE_TOOLS = [
|
|
3545
3685
|
"get_briefing",
|
|
3546
3686
|
"mem_save",
|
|
3547
3687
|
"mem_tried",
|
|
@@ -3552,7 +3692,47 @@ var ENFORCEMENT_PROFILE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
3552
3692
|
"code_map",
|
|
3553
3693
|
"pre_commit_check",
|
|
3554
3694
|
"mem_session_end"
|
|
3555
|
-
]
|
|
3695
|
+
];
|
|
3696
|
+
var MAINTENANCE_PROFILE_TOOLS = [
|
|
3697
|
+
...ENFORCEMENT_PROFILE_TOOLS,
|
|
3698
|
+
"mem_suggest_topic",
|
|
3699
|
+
"mem_for_files",
|
|
3700
|
+
"mem_list",
|
|
3701
|
+
"get_project_context",
|
|
3702
|
+
"bootstrap_project_save",
|
|
3703
|
+
"mem_resolve_project",
|
|
3704
|
+
"mem_update",
|
|
3705
|
+
"mem_approve",
|
|
3706
|
+
"mem_reject",
|
|
3707
|
+
"mem_pending",
|
|
3708
|
+
"mem_delete",
|
|
3709
|
+
"mem_diff",
|
|
3710
|
+
"get_recap",
|
|
3711
|
+
"code_search",
|
|
3712
|
+
"anti_patterns_check",
|
|
3713
|
+
"mem_distill",
|
|
3714
|
+
"mem_timeline",
|
|
3715
|
+
"mem_conflict_candidates"
|
|
3716
|
+
];
|
|
3717
|
+
var EXPERIMENTAL_PROFILE_TOOLS = [
|
|
3718
|
+
...MAINTENANCE_PROFILE_TOOLS,
|
|
3719
|
+
"mem_observe",
|
|
3720
|
+
"why_this_file",
|
|
3721
|
+
"why_this_decision",
|
|
3722
|
+
"mem_conflicts_with",
|
|
3723
|
+
"pattern_detect",
|
|
3724
|
+
"runtime_journal_append",
|
|
3725
|
+
"runtime_journal_tail"
|
|
3726
|
+
];
|
|
3727
|
+
var TOOL_PROFILES = {
|
|
3728
|
+
enforcement: new Set(ENFORCEMENT_PROFILE_TOOLS),
|
|
3729
|
+
maintenance: new Set(MAINTENANCE_PROFILE_TOOLS),
|
|
3730
|
+
experimental: new Set(EXPERIMENTAL_PROFILE_TOOLS)
|
|
3731
|
+
};
|
|
3732
|
+
function getAllowedToolsForProfile(profile) {
|
|
3733
|
+
if (profile === "full") return TOOL_PROFILES.experimental;
|
|
3734
|
+
return TOOL_PROFILES[profile] ?? TOOL_PROFILES.enforcement;
|
|
3735
|
+
}
|
|
3556
3736
|
var BRIEFING_TOOLS = /* @__PURE__ */ new Set(["get_briefing", "mem_relevant_to"]);
|
|
3557
3737
|
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
3558
3738
|
"mem_save",
|
|
@@ -3579,7 +3759,8 @@ function createHaiveServer(options = {}) {
|
|
|
3579
3759
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
3580
3760
|
{ capabilities: { tools: {}, prompts: {} } }
|
|
3581
3761
|
);
|
|
3582
|
-
const
|
|
3762
|
+
const allowedTools = getAllowedToolsForProfile(toolProfile);
|
|
3763
|
+
const shouldRegisterTool = (name) => allowedTools.has(name);
|
|
3583
3764
|
const registerTool = (name, description, schema, handler) => {
|
|
3584
3765
|
if (!shouldRegisterTool(name)) return;
|
|
3585
3766
|
const tool = server.tool.bind(server);
|
|
@@ -3603,7 +3784,11 @@ function createHaiveServer(options = {}) {
|
|
|
3603
3784
|
}
|
|
3604
3785
|
);
|
|
3605
3786
|
};
|
|
3606
|
-
const shouldRegisterPrompt = (name) =>
|
|
3787
|
+
const shouldRegisterPrompt = (name) => {
|
|
3788
|
+
if (name === "bootstrap_project" || name === "post_task") return true;
|
|
3789
|
+
if (name === "import_docs") return toolProfile !== "enforcement";
|
|
3790
|
+
return toolProfile === "experimental" || toolProfile === "full";
|
|
3791
|
+
};
|
|
3607
3792
|
registerTool(
|
|
3608
3793
|
"mem_save",
|
|
3609
3794
|
[
|