@hivelore/mcp 0.43.2 → 0.46.0
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 +1 -1
- package/dist/index.js +72 -13
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +74 -14
- package/dist/server.js.map +1 -1
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -420,7 +420,7 @@ Post-task reflection checklist. Guides the AI through capturing failed approache
|
|
|
420
420
|
```
|
|
421
421
|
Use the post_task prompt with:
|
|
422
422
|
task_summary: "Added Stripe payment integration"
|
|
423
|
-
files_touched: ["src/payments/StripeService.ts", "src/payments/PaymentController.ts"]
|
|
423
|
+
files_touched: '["src/payments/StripeService.ts", "src/payments/PaymentController.ts"]'
|
|
424
424
|
```
|
|
425
425
|
|
|
426
426
|
### `bootstrap_project`
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { mkdir as mkdir8, writeFile as writeFile15 } from "fs/promises";
|
|
7
|
+
import path15 from "path";
|
|
6
8
|
|
|
7
9
|
// src/context.ts
|
|
8
10
|
import { findProjectRoot, resolveHaivePaths } from "@hivelore/core";
|
|
@@ -1793,15 +1795,24 @@ async function memTried(input, ctx) {
|
|
|
1793
1795
|
import { existsSync as existsSync17, statSync } from "fs";
|
|
1794
1796
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile10 } from "fs/promises";
|
|
1795
1797
|
import path8 from "path";
|
|
1798
|
+
import { execFile } from "child_process";
|
|
1799
|
+
import { promisify } from "util";
|
|
1796
1800
|
import { z as z17 } from "zod";
|
|
1797
1801
|
import {
|
|
1798
1802
|
buildProposeCommand,
|
|
1803
|
+
incidentHintsFromDiff,
|
|
1799
1804
|
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
1800
1805
|
normalizeFramework,
|
|
1801
1806
|
parseLessonFields,
|
|
1802
1807
|
pickTestFramework,
|
|
1803
1808
|
scaffoldPostIncidentTest
|
|
1804
1809
|
} from "@hivelore/core";
|
|
1810
|
+
var execFileAsync = promisify(execFile);
|
|
1811
|
+
async function gitDiffText(root, redRef, paths) {
|
|
1812
|
+
const args = ["diff", redRef, "HEAD", "--", ...paths];
|
|
1813
|
+
const { stdout } = await execFileAsync("git", args, { cwd: root, maxBuffer: 64 * 1024 * 1024 });
|
|
1814
|
+
return stdout;
|
|
1815
|
+
}
|
|
1805
1816
|
var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
|
|
1806
1817
|
async function detectForAnchor(root, rel) {
|
|
1807
1818
|
let dir = path8.resolve(root, rel);
|
|
@@ -1848,6 +1859,9 @@ var ScaffoldTestInputSchema = {
|
|
|
1848
1859
|
memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
|
|
1849
1860
|
framework: z17.enum(["vitest", "jest", "pytest", "gotest"]).optional().describe("Test framework. Auto-detected from the package that owns the lesson's anchor paths when omitted."),
|
|
1850
1861
|
out_path: z17.string().optional().describe("Override the generated test file path (repo-relative)."),
|
|
1862
|
+
red_ref: z17.string().optional().describe(
|
|
1863
|
+
"Pre-fix incident commit/ref. When set, the scaffold names the symbols the fix (<red_ref>..HEAD) touched within the lesson's anchor scope and pre-fills the example around them, so the assertion is a targeted edit rather than a blank page. A bad ref falls back to the generic template."
|
|
1864
|
+
),
|
|
1851
1865
|
write: z17.boolean().default(true).describe("Write the file to disk (default). false = return the content for preview without writing.")
|
|
1852
1866
|
};
|
|
1853
1867
|
async function scaffoldTest(input, ctx) {
|
|
@@ -1861,13 +1875,23 @@ async function scaffoldTest(input, ctx) {
|
|
|
1861
1875
|
const groups = input.out_path ? allGroups.slice(0, 1) : allGroups;
|
|
1862
1876
|
const frameworkFor = (detected) => input.framework ? normalizeFramework(input.framework) ?? detected : detected;
|
|
1863
1877
|
const fields = parseLessonFields(found.memory.body);
|
|
1878
|
+
let incidentHints;
|
|
1879
|
+
if (input.red_ref) {
|
|
1880
|
+
try {
|
|
1881
|
+
const diff = await gitDiffText(ctx.paths.root, input.red_ref, anchorPaths);
|
|
1882
|
+
const hints = incidentHintsFromDiff(diff, { redRef: input.red_ref });
|
|
1883
|
+
if (hints.changedSymbols.length > 0 || hints.changedFiles.length > 0) incidentHints = hints;
|
|
1884
|
+
} catch {
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1864
1887
|
const lesson = {
|
|
1865
1888
|
memoryId: input.memory_id,
|
|
1866
1889
|
title: fields.title || input.memory_id,
|
|
1867
1890
|
whyFailed: fields.whyFailed,
|
|
1868
1891
|
instead: fields.instead,
|
|
1869
1892
|
incident: found.memory.frontmatter.sensor?.incident,
|
|
1870
|
-
paths: anchorPaths
|
|
1893
|
+
paths: anchorPaths,
|
|
1894
|
+
incidentHints
|
|
1871
1895
|
};
|
|
1872
1896
|
let scaffolds = groups.map(
|
|
1873
1897
|
(g) => scaffoldPostIncidentTest(lesson, { framework: frameworkFor(g.framework), outPath: input.out_path, baseDir: g.baseDir })
|
|
@@ -3191,7 +3215,7 @@ function oneLine(value) {
|
|
|
3191
3215
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
3192
3216
|
}
|
|
3193
3217
|
function serverVersion() {
|
|
3194
|
-
return true ? "0.
|
|
3218
|
+
return true ? "0.46.0" : "dev";
|
|
3195
3219
|
}
|
|
3196
3220
|
|
|
3197
3221
|
// src/tools/code-map.ts
|
|
@@ -3477,7 +3501,8 @@ var AntiPatternsCheckInputSchema = {
|
|
|
3477
3501
|
),
|
|
3478
3502
|
min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
|
|
3479
3503
|
"Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
|
|
3480
|
-
)
|
|
3504
|
+
),
|
|
3505
|
+
track: z26.boolean().default(true).describe("Record real prevention outcomes. Set false for eval/selftest probes so synthetic cases never inflate ROI.")
|
|
3481
3506
|
};
|
|
3482
3507
|
function tokenizeDiffForLiteral(diff) {
|
|
3483
3508
|
const lines = diff.split("\n");
|
|
@@ -3505,6 +3530,7 @@ function isHaiveOwnedPath(p) {
|
|
|
3505
3530
|
if (/^\.github\/workflows\/haive-.*\.ya?ml$/.test(p)) return true;
|
|
3506
3531
|
return false;
|
|
3507
3532
|
}
|
|
3533
|
+
var MAX_FUZZY_SCAN_LINES = 2e4;
|
|
3508
3534
|
var TEST_PATH_RE = /(?:^|\/)(?:tests?|__tests__|__mocks__|e2e|fixtures)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/i;
|
|
3509
3535
|
function isTestPath(p) {
|
|
3510
3536
|
return TEST_PATH_RE.test(p);
|
|
@@ -3515,7 +3541,7 @@ function stripTestHunks(diff) {
|
|
|
3515
3541
|
let block = [];
|
|
3516
3542
|
let keep = true;
|
|
3517
3543
|
const flush = () => {
|
|
3518
|
-
if (keep) out.push(
|
|
3544
|
+
if (keep) for (const l of block) out.push(l);
|
|
3519
3545
|
block = [];
|
|
3520
3546
|
keep = true;
|
|
3521
3547
|
};
|
|
@@ -3536,7 +3562,7 @@ function stripAiDirHunks(diff) {
|
|
|
3536
3562
|
let block = [];
|
|
3537
3563
|
let keep = true;
|
|
3538
3564
|
const flush = () => {
|
|
3539
|
-
if (keep) out.push(
|
|
3565
|
+
if (keep) for (const l of block) out.push(l);
|
|
3540
3566
|
block = [];
|
|
3541
3567
|
keep = true;
|
|
3542
3568
|
};
|
|
@@ -3607,7 +3633,10 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3607
3633
|
}
|
|
3608
3634
|
}
|
|
3609
3635
|
const scanDiff = input.diff ? stripTestHunks(stripAiDirHunks(input.diff)) : input.diff;
|
|
3610
|
-
|
|
3636
|
+
const scanAddedLineCount = scanDiff ? addedLinesFromDiff(scanDiff).split("\n").length : 0;
|
|
3637
|
+
const fuzzyScanTooLarge = scanAddedLineCount > MAX_FUZZY_SCAN_LINES;
|
|
3638
|
+
const notice = fuzzyScanTooLarge ? `Diff is very large (${scanAddedLineCount.toLocaleString()} added lines) \u2014 literal/semantic corroboration skipped for performance; anchored lessons and deterministic sensors were still evaluated in full. If this staged node_modules or a build artifact, unstage it.` : void 0;
|
|
3639
|
+
if (scanDiff && !fuzzyScanTooLarge) {
|
|
3611
3640
|
const tokens = tokenizeDiffForLiteral(scanDiff);
|
|
3612
3641
|
const added = addedLinesFromDiff(scanDiff);
|
|
3613
3642
|
const addedText = added.trim().length > 0 ? added : scanDiff;
|
|
@@ -3642,7 +3671,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3642
3671
|
}
|
|
3643
3672
|
}
|
|
3644
3673
|
}
|
|
3645
|
-
if (input.semantic && scanDiff) {
|
|
3674
|
+
if (input.semantic && scanDiff && !fuzzyScanTooLarge) {
|
|
3646
3675
|
try {
|
|
3647
3676
|
const mod = await import("@hivelore/embeddings");
|
|
3648
3677
|
const added = addedLinesFromDiff(scanDiff);
|
|
@@ -3670,10 +3699,13 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3670
3699
|
}).slice(0, input.limit);
|
|
3671
3700
|
const isHardBlockCatch = (w) => w.reasons.includes("sensor");
|
|
3672
3701
|
const strongCatches = warnings.filter(isHardBlockCatch);
|
|
3673
|
-
|
|
3702
|
+
if (input.track !== false) {
|
|
3703
|
+
await recordPreventionHits(ctx.paths, strongCatches.map((w) => w.id), "anti-pattern");
|
|
3704
|
+
}
|
|
3674
3705
|
return {
|
|
3675
3706
|
scanned: negative.length,
|
|
3676
|
-
warnings
|
|
3707
|
+
warnings,
|
|
3708
|
+
...notice ? { notice } : {}
|
|
3677
3709
|
};
|
|
3678
3710
|
}
|
|
3679
3711
|
|
|
@@ -3901,6 +3933,7 @@ async function preCommitCheck(input, ctx) {
|
|
|
3901
3933
|
relevant_memories: relevant_memories.length,
|
|
3902
3934
|
stale_anchors: staleHits.length
|
|
3903
3935
|
},
|
|
3936
|
+
...apResult.notice ? { notice: apResult.notice } : {},
|
|
3904
3937
|
warnings: classifiedWarnings,
|
|
3905
3938
|
relevant_memories,
|
|
3906
3939
|
stale_anchors: staleHits.map((r) => {
|
|
@@ -4452,13 +4485,14 @@ Main code areas detected: ${areas}
|
|
|
4452
4485
|
import { z as z35 } from "zod";
|
|
4453
4486
|
var PostTaskArgsSchema = {
|
|
4454
4487
|
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
4455
|
-
files_touched: z35.
|
|
4488
|
+
files_touched: z35.string().optional().describe("Files you created or modified during the task, as CSV or a JSON array string")
|
|
4456
4489
|
};
|
|
4457
4490
|
function postTaskPrompt(args, ctx) {
|
|
4458
4491
|
const taskLine = args.task_summary ? `
|
|
4459
4492
|
Task just completed: **${args.task_summary}**` : "";
|
|
4460
|
-
const
|
|
4461
|
-
|
|
4493
|
+
const filesTouched = parsePromptFilesTouched(args.files_touched);
|
|
4494
|
+
const filesLine = filesTouched.length > 0 ? `
|
|
4495
|
+
Files touched: ${filesTouched.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
4462
4496
|
const text = `You have just finished a task. Before closing this session, take 60 seconds to capture what you learned.
|
|
4463
4497
|
${taskLine}${filesLine}
|
|
4464
4498
|
|
|
@@ -4547,6 +4581,20 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
4547
4581
|
]
|
|
4548
4582
|
};
|
|
4549
4583
|
}
|
|
4584
|
+
function parsePromptFilesTouched(input) {
|
|
4585
|
+
const raw = input?.trim();
|
|
4586
|
+
if (!raw) return [];
|
|
4587
|
+
if (raw.startsWith("[")) {
|
|
4588
|
+
try {
|
|
4589
|
+
const parsed2 = JSON.parse(raw);
|
|
4590
|
+
if (Array.isArray(parsed2)) {
|
|
4591
|
+
return parsed2.filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
|
|
4592
|
+
}
|
|
4593
|
+
} catch {
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
return raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
4597
|
+
}
|
|
4550
4598
|
|
|
4551
4599
|
// src/prompts/import-docs.ts
|
|
4552
4600
|
import { z as z36 } from "zod";
|
|
@@ -4620,7 +4668,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4620
4668
|
// src/server.ts
|
|
4621
4669
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
4622
4670
|
var SERVER_NAME = "hivelore";
|
|
4623
|
-
var SERVER_VERSION = "0.
|
|
4671
|
+
var SERVER_VERSION = "0.46.0";
|
|
4624
4672
|
function jsonResult(data) {
|
|
4625
4673
|
return {
|
|
4626
4674
|
content: [
|
|
@@ -5559,11 +5607,22 @@ function printHaiveMcpVersion() {
|
|
|
5559
5607
|
}
|
|
5560
5608
|
async function runHaiveMcpStdio(options) {
|
|
5561
5609
|
const { server, context } = createHaiveServer({ root: options.root, env: process.env });
|
|
5610
|
+
await writeMcpRuntimeMarker(context).catch(() => {
|
|
5611
|
+
});
|
|
5562
5612
|
console.error(
|
|
5563
5613
|
`[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
|
|
5564
5614
|
);
|
|
5565
5615
|
await server.connect(new StdioServerTransport());
|
|
5566
5616
|
}
|
|
5617
|
+
async function writeMcpRuntimeMarker(context) {
|
|
5618
|
+
await mkdir8(context.paths.runtimeDir, { recursive: true });
|
|
5619
|
+
await writeFile15(path15.join(context.paths.runtimeDir, "mcp-server.json"), JSON.stringify({
|
|
5620
|
+
version: SERVER_VERSION,
|
|
5621
|
+
pid: process.pid,
|
|
5622
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5623
|
+
command: "hivelore mcp --stdio"
|
|
5624
|
+
}, null, 2), "utf8");
|
|
5625
|
+
}
|
|
5567
5626
|
|
|
5568
5627
|
// src/index.ts
|
|
5569
5628
|
var parsed = parseMcpCliArgs(process.argv);
|