@hivelore/cli 0.42.1 → 0.44.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 +7 -6
- package/dist/{chunk-EJ7A4IKD.js → chunk-6KRXMDLC.js} +135 -44
- package/dist/chunk-6KRXMDLC.js.map +1 -0
- package/dist/index.js +327 -74
- package/dist/index.js.map +1 -1
- package/dist/{server-PZWIQUU7.js → server-MCNCAFJN.js} +6 -4
- package/package.json +21 -6
- package/dist/chunk-EJ7A4IKD.js.map +0 -1
- /package/dist/{server-PZWIQUU7.js.map → server-MCNCAFJN.js.map} +0 -0
package/README.md
CHANGED
|
@@ -233,6 +233,7 @@ Run the repeatable quality gate for Hivelore itself or for a project using Hivel
|
|
|
233
233
|
```bash
|
|
234
234
|
hivelore eval
|
|
235
235
|
hivelore eval --semantic-only
|
|
236
|
+
hivelore eval --semantic-ranking # require and exercise the real embeddings-backed ranker
|
|
236
237
|
hivelore eval --spec .ai/eval/spec.json --fail-under 80
|
|
237
238
|
```
|
|
238
239
|
|
|
@@ -533,14 +534,14 @@ hivelore install-hooks --dir /path/to/project
|
|
|
533
534
|
|
|
534
535
|
---
|
|
535
536
|
|
|
536
|
-
### `hivelore
|
|
537
|
+
### `hivelore index`
|
|
537
538
|
|
|
538
539
|
Manage the local semantic search index (requires `@hivelore/embeddings` to be installed).
|
|
539
540
|
|
|
540
541
|
```bash
|
|
541
|
-
hivelore
|
|
542
|
-
hivelore
|
|
543
|
-
hivelore
|
|
542
|
+
hivelore index memories # Build or refresh the embeddings index
|
|
543
|
+
hivelore index status # Show index stats (count, last updated, model)
|
|
544
|
+
hivelore index query "how do we handle retries on payment failures"
|
|
544
545
|
```
|
|
545
546
|
|
|
546
547
|
The model (`bge-small-en-v1.5`, ~110MB) is downloaded on first use and cached locally. **No data leaves your machine.**
|
|
@@ -655,8 +656,8 @@ Install `@hivelore/embeddings` for similarity-based memory retrieval:
|
|
|
655
656
|
|
|
656
657
|
```bash
|
|
657
658
|
npm install -g @hivelore/embeddings
|
|
658
|
-
hivelore
|
|
659
|
-
hivelore
|
|
659
|
+
hivelore index memories # First run downloads the model (~110MB)
|
|
660
|
+
hivelore index query "payment retry logic"
|
|
660
661
|
```
|
|
661
662
|
|
|
662
663
|
From MCP: set `semantic: true` on `mem_search` or `get_briefing`.
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// ../mcp/dist/server.js
|
|
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
|
import { findProjectRoot, resolveHaivePaths } from "@hivelore/core";
|
|
7
9
|
import { mkdir, writeFile } from "fs/promises";
|
|
8
10
|
import { existsSync } from "fs";
|
|
@@ -133,7 +135,7 @@ import {
|
|
|
133
135
|
suggestSensorSeed as suggestSensorSeed2
|
|
134
136
|
} from "@hivelore/core";
|
|
135
137
|
import { z as z16 } from "zod";
|
|
136
|
-
import {
|
|
138
|
+
import { execFileSync } from "child_process";
|
|
137
139
|
import { readFile as readFile3, writeFile as writeFile8 } from "fs/promises";
|
|
138
140
|
import { existsSync as existsSync15, rmSync, symlinkSync } from "fs";
|
|
139
141
|
import os from "os";
|
|
@@ -193,7 +195,7 @@ import {
|
|
|
193
195
|
import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
|
|
194
196
|
import { existsSync as existsSync19 } from "fs";
|
|
195
197
|
import path10 from "path";
|
|
196
|
-
import { execSync
|
|
198
|
+
import { execSync } from "child_process";
|
|
197
199
|
import { readFile as readFile7, writeFile as writeFile14, readdir as readdir4 } from "fs/promises";
|
|
198
200
|
import { existsSync as existsSync22 } from "fs";
|
|
199
201
|
import path13 from "path";
|
|
@@ -305,6 +307,7 @@ import { z as z32 } from "zod";
|
|
|
305
307
|
import { z as z33 } from "zod";
|
|
306
308
|
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
307
309
|
import { existsSync as existsSync29 } from "fs";
|
|
310
|
+
import path14 from "path";
|
|
308
311
|
import {
|
|
309
312
|
assessBootstrapState as assessBootstrapState2,
|
|
310
313
|
loadCodeMap as loadCodeMap4,
|
|
@@ -1283,10 +1286,32 @@ async function memApprove(input, ctx) {
|
|
|
1283
1286
|
};
|
|
1284
1287
|
}
|
|
1285
1288
|
var cachedEngine;
|
|
1289
|
+
var registeredDynamicLanguages = /* @__PURE__ */ new Set();
|
|
1290
|
+
async function loadDynamicLanguages(engine) {
|
|
1291
|
+
const registrations = {};
|
|
1292
|
+
const candidates = [
|
|
1293
|
+
["python", () => import("@ast-grep/lang-python")],
|
|
1294
|
+
["go", () => import("@ast-grep/lang-go")],
|
|
1295
|
+
["rust", () => import("@ast-grep/lang-rust")],
|
|
1296
|
+
["java", () => import("@ast-grep/lang-java")]
|
|
1297
|
+
];
|
|
1298
|
+
for (const [name, load] of candidates) {
|
|
1299
|
+
try {
|
|
1300
|
+
const mod = await load();
|
|
1301
|
+
registrations[name] = mod.default;
|
|
1302
|
+
registeredDynamicLanguages.add(name);
|
|
1303
|
+
} catch {
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
if (Object.keys(registrations).length > 0) {
|
|
1307
|
+
engine.registerDynamicLanguage(registrations);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1286
1310
|
async function loadAstEngine() {
|
|
1287
1311
|
if (cachedEngine !== void 0) return cachedEngine;
|
|
1288
1312
|
try {
|
|
1289
1313
|
cachedEngine = await import("@ast-grep/napi");
|
|
1314
|
+
await loadDynamicLanguages(cachedEngine);
|
|
1290
1315
|
} catch {
|
|
1291
1316
|
cachedEngine = null;
|
|
1292
1317
|
}
|
|
@@ -1295,11 +1320,22 @@ async function loadAstEngine() {
|
|
|
1295
1320
|
async function astEngineAvailable() {
|
|
1296
1321
|
return await loadAstEngine() !== null;
|
|
1297
1322
|
}
|
|
1298
|
-
function astLangForPath(filePath) {
|
|
1323
|
+
function astLangForPath(filePath, explicitLanguage) {
|
|
1324
|
+
if (explicitLanguage) {
|
|
1325
|
+
const normalized = explicitLanguage.toLowerCase();
|
|
1326
|
+
if (["typescript", "tsx", "javascript", "html", "css"].includes(normalized)) {
|
|
1327
|
+
return normalized === "typescript" ? "TypeScript" : normalized === "tsx" ? "Tsx" : normalized === "javascript" ? "JavaScript" : normalized[0].toUpperCase() + normalized.slice(1);
|
|
1328
|
+
}
|
|
1329
|
+
return registeredDynamicLanguages.has(normalized) ? normalized : null;
|
|
1330
|
+
}
|
|
1299
1331
|
const ext = path5.extname(filePath).toLowerCase();
|
|
1300
1332
|
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "TypeScript";
|
|
1301
1333
|
if (ext === ".tsx") return "Tsx";
|
|
1302
1334
|
if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") return "JavaScript";
|
|
1335
|
+
if (ext === ".py" && registeredDynamicLanguages.has("python")) return "python";
|
|
1336
|
+
if (ext === ".go" && registeredDynamicLanguages.has("go")) return "go";
|
|
1337
|
+
if (ext === ".rs" && registeredDynamicLanguages.has("rust")) return "rust";
|
|
1338
|
+
if (ext === ".java" && registeredDynamicLanguages.has("java")) return "java";
|
|
1303
1339
|
return null;
|
|
1304
1340
|
}
|
|
1305
1341
|
function absentPresentInNode(node, absent) {
|
|
@@ -1314,12 +1350,12 @@ function absentPresentInNode(node, absent) {
|
|
|
1314
1350
|
return text.includes(absent);
|
|
1315
1351
|
}
|
|
1316
1352
|
}
|
|
1317
|
-
async function runAstPattern(content, filePath, pattern, absent) {
|
|
1353
|
+
async function runAstPattern(content, filePath, pattern, absent, rule, language) {
|
|
1318
1354
|
const engine = await loadAstEngine();
|
|
1319
1355
|
if (!engine) return { status: "engine-missing", matches: [] };
|
|
1320
|
-
const langName = astLangForPath(filePath);
|
|
1356
|
+
const langName = astLangForPath(filePath, language);
|
|
1321
1357
|
if (!langName) return { status: "unsupported-language", matches: [] };
|
|
1322
|
-
const lang = engine.Lang[langName];
|
|
1358
|
+
const lang = langName in engine.Lang ? engine.Lang[langName] : langName;
|
|
1323
1359
|
let root;
|
|
1324
1360
|
try {
|
|
1325
1361
|
root = engine.parse(lang, content).root();
|
|
@@ -1328,7 +1364,9 @@ async function runAstPattern(content, filePath, pattern, absent) {
|
|
|
1328
1364
|
}
|
|
1329
1365
|
let nodes;
|
|
1330
1366
|
try {
|
|
1331
|
-
|
|
1367
|
+
const matcher = rule ? { rule: pattern ? { all: [{ pattern }, rule] } : rule } : pattern;
|
|
1368
|
+
if (!matcher) return { status: "invalid-pattern", matches: [], detail: "missing pattern/rule" };
|
|
1369
|
+
nodes = root.findAll(matcher);
|
|
1332
1370
|
} catch (err) {
|
|
1333
1371
|
return { status: "invalid-pattern", matches: [], detail: String(err).slice(0, 200) };
|
|
1334
1372
|
}
|
|
@@ -1345,7 +1383,7 @@ async function runAstPattern(content, filePath, pattern, absent) {
|
|
|
1345
1383
|
return { status: "ok", matches };
|
|
1346
1384
|
}
|
|
1347
1385
|
async function runAstSensorOnContent(input) {
|
|
1348
|
-
const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent);
|
|
1386
|
+
const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent, input.rule, input.language);
|
|
1349
1387
|
if (scan.status !== "ok" || !input.addedLines || input.addedLines.size === 0) return scan;
|
|
1350
1388
|
const added = input.addedLines;
|
|
1351
1389
|
return {
|
|
@@ -1363,7 +1401,9 @@ var ProposeSensorInputSchema = {
|
|
|
1363
1401
|
kind: z15.enum(["regex", "ast", "shell", "test"]).default("regex").describe(
|
|
1364
1402
|
"regex = pattern matched on added diff lines (default). ast = an ast-grep STRUCTURAL pattern (e.g. 'stripe.paymentIntents.create($$$)') matched on the AST of changed files \u2014 comments and strings can never false-positive; `absent` is a sub-pattern that must be missing INSIDE the match (requires the optional @ast-grep/napi engine). shell|test = a COMMAND the gate runs when the diff touches the sensor's paths \u2014 routes the team's own oracle (an existing test, an invariant script) to this lesson. Command sensors only execute where enforcement.runCommandSensors=true."
|
|
1365
1403
|
),
|
|
1366
|
-
pattern: z15.string().optional().describe("kind=regex: regex matching the
|
|
1404
|
+
pattern: z15.string().optional().describe("kind=regex: regex matching the faulty usage; kind=ast: optional structural pattern (may be combined with `rule`)."),
|
|
1405
|
+
rule: z15.record(z15.unknown()).optional().describe("kind=ast: full ast-grep Rule object (kind/inside/has/not/all/any/etc.). May be used alone or combined with pattern."),
|
|
1406
|
+
language: z15.string().optional().describe("kind=ast: explicit built-in/dynamic language name for non-standard file extensions."),
|
|
1367
1407
|
command: z15.string().optional().describe("kind=shell|test: command to execute (e.g. 'npx vitest run tests/payments/refund.spec.ts'). Non-zero exit = the lesson fires."),
|
|
1368
1408
|
timeout_ms: z15.number().int().positive().optional().describe("kind=shell|test: max runtime before the executor kills the command (default 120000)."),
|
|
1369
1409
|
absent: z15.string().optional().describe(
|
|
@@ -1394,7 +1434,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
1394
1434
|
const targets = [];
|
|
1395
1435
|
for (const rel of relPaths) {
|
|
1396
1436
|
try {
|
|
1397
|
-
const content =
|
|
1437
|
+
const content = execFileSync("git", ["show", `HEAD:./${rel}`], {
|
|
1398
1438
|
cwd: root,
|
|
1399
1439
|
encoding: "utf8",
|
|
1400
1440
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -1415,7 +1455,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
1415
1455
|
}
|
|
1416
1456
|
function runCommandForValidation(command, root, timeoutMs = 12e4) {
|
|
1417
1457
|
try {
|
|
1418
|
-
|
|
1458
|
+
execFileSync("bash", ["-c", command], {
|
|
1419
1459
|
cwd: root,
|
|
1420
1460
|
timeout: timeoutMs,
|
|
1421
1461
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -1440,7 +1480,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
|
|
|
1440
1480
|
let added = false;
|
|
1441
1481
|
try {
|
|
1442
1482
|
try {
|
|
1443
|
-
|
|
1483
|
+
execFileSync("git", ["worktree", "add", "--detach", worktree, redRef], {
|
|
1444
1484
|
cwd: root,
|
|
1445
1485
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1446
1486
|
timeout: 6e4
|
|
@@ -1467,7 +1507,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
|
|
|
1467
1507
|
} finally {
|
|
1468
1508
|
if (added) {
|
|
1469
1509
|
try {
|
|
1470
|
-
|
|
1510
|
+
execFileSync("git", ["worktree", "remove", "--force", worktree], { cwd: root, stdio: "ignore", timeout: 6e4 });
|
|
1471
1511
|
} catch {
|
|
1472
1512
|
try {
|
|
1473
1513
|
rmSync(worktree, { recursive: true, force: true });
|
|
@@ -1507,13 +1547,13 @@ async function proposeSensor(input, ctx) {
|
|
|
1507
1547
|
};
|
|
1508
1548
|
}
|
|
1509
1549
|
} else if (kind === "ast") {
|
|
1510
|
-
if (!input.pattern?.trim()) {
|
|
1550
|
+
if (!input.pattern?.trim() && !input.rule) {
|
|
1511
1551
|
return {
|
|
1512
1552
|
accepted: false,
|
|
1513
1553
|
memory_id: input.memory_id,
|
|
1514
1554
|
severity: input.severity,
|
|
1515
1555
|
reason: "invalid-pattern",
|
|
1516
|
-
guidance: "kind=ast requires a `pattern`
|
|
1556
|
+
guidance: "kind=ast requires a structural `pattern` or a full ast-grep `rule` object.",
|
|
1517
1557
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
|
|
1518
1558
|
};
|
|
1519
1559
|
}
|
|
@@ -1534,7 +1574,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1534
1574
|
}
|
|
1535
1575
|
const personalScopeNudge = found.memory.frontmatter.scope === "personal" ? ` Note: this lesson is personal-scoped, so the sensor guards only YOUR machine (personal memories are gitignored). Promote it so the gate travels with the repo: hivelore memory promote ${input.memory_id}.` : "";
|
|
1536
1576
|
if (kind === "ast") {
|
|
1537
|
-
const pattern = input.pattern
|
|
1577
|
+
const pattern = input.pattern?.trim();
|
|
1538
1578
|
if (!await astEngineAvailable() && input.severity === "block") {
|
|
1539
1579
|
return {
|
|
1540
1580
|
accepted: false,
|
|
@@ -1545,7 +1585,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1545
1585
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
|
|
1546
1586
|
};
|
|
1547
1587
|
}
|
|
1548
|
-
const brittleAst = sensorPatternBrittleness(pattern);
|
|
1588
|
+
const brittleAst = pattern ? sensorPatternBrittleness(pattern) : null;
|
|
1549
1589
|
if (brittleAst && input.severity === "block") {
|
|
1550
1590
|
return {
|
|
1551
1591
|
accepted: false,
|
|
@@ -1560,7 +1600,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1560
1600
|
const currentTargetsAst = await readPresumedCorrectTargets(ctx.paths.root, anchorPathsAst);
|
|
1561
1601
|
const firedOnAst = [];
|
|
1562
1602
|
for (const target of currentTargetsAst) {
|
|
1563
|
-
const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: target.content, filePath: target.path });
|
|
1603
|
+
const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: target.content, filePath: target.path });
|
|
1564
1604
|
if (scan.status === "invalid-pattern") {
|
|
1565
1605
|
return {
|
|
1566
1606
|
accepted: false,
|
|
@@ -1589,10 +1629,10 @@ async function proposeSensor(input, ctx) {
|
|
|
1589
1629
|
];
|
|
1590
1630
|
let firesOnBadAst = null;
|
|
1591
1631
|
if (badExamplesAst.length > 0 && await astEngineAvailable()) {
|
|
1592
|
-
const exampleLang = anchorPathsAst.find((p) => astLangForPath(p) !== null) ?? "example.tsx";
|
|
1632
|
+
const exampleLang = anchorPathsAst.find((p) => astLangForPath(p, input.language) !== null) ?? "example.tsx";
|
|
1593
1633
|
firesOnBadAst = false;
|
|
1594
1634
|
for (const example of badExamplesAst) {
|
|
1595
|
-
const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: example, filePath: exampleLang });
|
|
1635
|
+
const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: example, filePath: exampleLang });
|
|
1596
1636
|
if (scan.status === "ok" && scan.matches.length > 0) {
|
|
1597
1637
|
firesOnBadAst = true;
|
|
1598
1638
|
break;
|
|
@@ -1611,10 +1651,12 @@ async function proposeSensor(input, ctx) {
|
|
|
1611
1651
|
}
|
|
1612
1652
|
const sensorAst = {
|
|
1613
1653
|
kind: "ast",
|
|
1614
|
-
pattern,
|
|
1654
|
+
...pattern ? { pattern } : {},
|
|
1655
|
+
...input.rule ? { rule: input.rule } : {},
|
|
1656
|
+
...input.language ? { language: input.language } : {},
|
|
1615
1657
|
...input.absent ? { absent: input.absent } : {},
|
|
1616
1658
|
paths: anchorPathsAst,
|
|
1617
|
-
message: input.message?.trim() || deriveMessage(found.memory.body, pattern, input.absent),
|
|
1659
|
+
message: input.message?.trim() || deriveMessage(found.memory.body, pattern ?? "AST rule", input.absent),
|
|
1618
1660
|
...input.incident?.trim() ? { incident: input.incident.trim() } : {},
|
|
1619
1661
|
severity: input.severity,
|
|
1620
1662
|
autogen: false,
|
|
@@ -1677,6 +1719,16 @@ ${verdictCmd.detail}`,
|
|
|
1677
1719
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: anchorPathsCmd }
|
|
1678
1720
|
};
|
|
1679
1721
|
}
|
|
1722
|
+
if (input.severity === "block" && !input.red_ref?.trim()) {
|
|
1723
|
+
return {
|
|
1724
|
+
accepted: false,
|
|
1725
|
+
memory_id: input.memory_id,
|
|
1726
|
+
severity: input.severity,
|
|
1727
|
+
reason: "red-required",
|
|
1728
|
+
guidance: "A blocking shell/test sensor requires `red_ref`: the oracle must PASS on the current tree and FAIL on the pre-fix incident state. Pass the incident commit/ref, or propose the sensor at warn severity until RED can be proven.",
|
|
1729
|
+
self_check: { silent_on_current: true, fires_on_bad: null, fired_on: [] }
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1680
1732
|
const sensorCmd = {
|
|
1681
1733
|
kind,
|
|
1682
1734
|
command: input.command.trim(),
|
|
@@ -1698,7 +1750,7 @@ ${verdictCmd.detail}`,
|
|
|
1698
1750
|
accepted: true,
|
|
1699
1751
|
memory_id: input.memory_id,
|
|
1700
1752
|
severity: input.severity,
|
|
1701
|
-
guidance: (verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail}).`) + (redProven ? " RED proven: the oracle demonstrably FAILS on the incident state (red_ref) \u2014 recorded as red_proven." : "
|
|
1753
|
+
guidance: (verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail}).`) + (redProven ? " RED proven: the oracle demonstrably FAILS on the incident state (red_ref) \u2014 recorded as red_proven." : " This warn-only oracle is not RED-proven; pass red_ref before promoting it to block.") + (pendingTests.length > 0 ? ` Note: the routed test is still a PENDING stub (${pendingTests.join(", ")}) \u2014 it passes on anything; write the assertion to make this oracle real.` : "") + personalScopeNudge,
|
|
1702
1754
|
self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: redProven ? true : null, fired_on: [] },
|
|
1703
1755
|
file_path: found.filePath
|
|
1704
1756
|
};
|
|
@@ -1765,8 +1817,10 @@ var MemTriedInputSchema = {
|
|
|
1765
1817
|
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
1766
1818
|
author: z16.string().optional().describe("Author handle or email"),
|
|
1767
1819
|
sensor: z16.object({
|
|
1768
|
-
kind: z16.enum(["regex", "shell", "test"]).default("regex").describe("regex pattern, or a shell/test
|
|
1769
|
-
pattern: z16.string().optional().describe("kind=regex:
|
|
1820
|
+
kind: z16.enum(["regex", "ast", "shell", "test"]).default("regex").describe("regex/AST pattern, or a shell/test command"),
|
|
1821
|
+
pattern: z16.string().optional().describe("kind=regex|ast: pattern matching the faulty usage"),
|
|
1822
|
+
rule: z16.record(z16.unknown()).optional().describe("kind=ast: full ast-grep Rule object"),
|
|
1823
|
+
language: z16.string().optional().describe("kind=ast: explicit built-in/dynamic language"),
|
|
1770
1824
|
command: z16.string().optional().describe("kind=shell|test: command the gate runs when the diff touches the sensor's paths (non-zero exit = lesson fires)"),
|
|
1771
1825
|
timeout_ms: z16.number().int().positive().optional().describe("kind=shell|test: max runtime (default 120000)"),
|
|
1772
1826
|
absent: z16.string().optional().describe("kind=regex: regex marking CORRECT usage nearby \u2014 excludes it from firing"),
|
|
@@ -1816,6 +1870,8 @@ async function memTried(input, ctx) {
|
|
|
1816
1870
|
memory_id: frontmatter.id,
|
|
1817
1871
|
kind: input.sensor.kind ?? "regex",
|
|
1818
1872
|
pattern: input.sensor.pattern,
|
|
1873
|
+
rule: input.sensor.rule,
|
|
1874
|
+
language: input.sensor.language,
|
|
1819
1875
|
command: input.sensor.command,
|
|
1820
1876
|
timeout_ms: input.sensor.timeout_ms,
|
|
1821
1877
|
absent: input.sensor.absent,
|
|
@@ -2109,7 +2165,7 @@ var SessionTracker = class {
|
|
|
2109
2165
|
}
|
|
2110
2166
|
let gitDiff;
|
|
2111
2167
|
try {
|
|
2112
|
-
const raw =
|
|
2168
|
+
const raw = execSync("git diff HEAD", {
|
|
2113
2169
|
cwd: this.ctx.paths.root,
|
|
2114
2170
|
timeout: 5e3,
|
|
2115
2171
|
encoding: "utf8",
|
|
@@ -2141,7 +2197,7 @@ var SessionTracker = class {
|
|
|
2141
2197
|
const triedThreads = this.events.filter((e) => e.tool === "mem_tried").map((e) => e.summary ?? "").filter(Boolean);
|
|
2142
2198
|
let diffStat;
|
|
2143
2199
|
try {
|
|
2144
|
-
diffStat =
|
|
2200
|
+
diffStat = execSync("git diff --stat HEAD", {
|
|
2145
2201
|
cwd: this.ctx.paths.root,
|
|
2146
2202
|
timeout: 5e3,
|
|
2147
2203
|
encoding: "utf8",
|
|
@@ -2491,6 +2547,8 @@ var GetBriefingInputSchema = {
|
|
|
2491
2547
|
),
|
|
2492
2548
|
include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
2493
2549
|
track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
2550
|
+
memory_scopes: z20.array(z20.enum(["personal", "team", "module", "shared"])).optional().describe("Restrict the candidate corpus to selected scopes. Omit to include every scope."),
|
|
2551
|
+
deterministic: z20.boolean().optional().describe("Ignore machine-local usage/impact signals so repeated evaluations rank the shared corpus reproducibly."),
|
|
2494
2552
|
format: z20.enum(["full", "compact", "actions"]).default("full").describe(
|
|
2495
2553
|
"Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
|
|
2496
2554
|
),
|
|
@@ -2519,9 +2577,11 @@ async function getBriefing(input, ctx) {
|
|
|
2519
2577
|
let searchMode = "literal";
|
|
2520
2578
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
2521
2579
|
let byId = /* @__PURE__ */ new Map();
|
|
2580
|
+
const allowedScopes = input.memory_scopes ? new Set(input.memory_scopes) : null;
|
|
2581
|
+
const scopeAllowed = (loaded) => allowedScopes === null || allowedScopes.has(loaded.memory.frontmatter.scope);
|
|
2522
2582
|
let lastSession;
|
|
2523
2583
|
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2524
|
-
const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2584
|
+
const allLoaded = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
|
|
2525
2585
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
2526
2586
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2527
2587
|
);
|
|
@@ -2554,10 +2614,11 @@ async function getBriefing(input, ctx) {
|
|
|
2554
2614
|
if (memoryHasExcludedTag(memory.frontmatter, excludeTags)) return false;
|
|
2555
2615
|
return true;
|
|
2556
2616
|
});
|
|
2557
|
-
usage = await loadUsageIndex8(ctx.paths);
|
|
2617
|
+
usage = input.deterministic ? { version: 1, updated_at: "", by_id: {} } : await loadUsageIndex8(ctx.paths);
|
|
2558
2618
|
byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2619
|
+
const semanticEnabled = input.semantic && input.deterministic !== true;
|
|
2620
|
+
const semanticHits = input.task && semanticEnabled ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
2621
|
+
if (input.task && semanticEnabled) {
|
|
2561
2622
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
2562
2623
|
}
|
|
2563
2624
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -2893,7 +2954,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2893
2954
|
}
|
|
2894
2955
|
}
|
|
2895
2956
|
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2896
|
-
const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2957
|
+
const allMems = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
|
|
2897
2958
|
for (const { memory } of allMems) {
|
|
2898
2959
|
const fm = memory.frontmatter;
|
|
2899
2960
|
if (!fm.requires_human_approval) continue;
|
|
@@ -2950,7 +3011,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2950
3011
|
pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
|
|
2951
3012
|
} catch {
|
|
2952
3013
|
}
|
|
2953
|
-
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
|
|
3014
|
+
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed) : [];
|
|
2954
3015
|
const cmForBootstrap = await loadCodeMap(ctx.paths);
|
|
2955
3016
|
let existingModules = [];
|
|
2956
3017
|
try {
|
|
@@ -2961,7 +3022,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2961
3022
|
const bootstrap = assessBootstrapState({
|
|
2962
3023
|
projectContextRaw: pcRaw,
|
|
2963
3024
|
memories: allForBootstrap,
|
|
2964
|
-
codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files) : [],
|
|
3025
|
+
codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files).filter((file) => existsSync22(path13.join(ctx.paths.root, file))) : [],
|
|
2965
3026
|
existingModules
|
|
2966
3027
|
});
|
|
2967
3028
|
if (bootstrap.state !== "ready" && bootstrap.metrics.mainAreas > 0) {
|
|
@@ -3152,7 +3213,7 @@ function oneLine(value) {
|
|
|
3152
3213
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
3153
3214
|
}
|
|
3154
3215
|
function serverVersion() {
|
|
3155
|
-
return true ? "0.
|
|
3216
|
+
return true ? "0.44.0" : "dev";
|
|
3156
3217
|
}
|
|
3157
3218
|
var CodeMapInputSchema = {
|
|
3158
3219
|
file: z21.string().optional().describe("Filter to files whose path contains this substring"),
|
|
@@ -3395,7 +3456,8 @@ var AntiPatternsCheckInputSchema = {
|
|
|
3395
3456
|
),
|
|
3396
3457
|
min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
|
|
3397
3458
|
"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."
|
|
3398
|
-
)
|
|
3459
|
+
),
|
|
3460
|
+
track: z26.boolean().default(true).describe("Record real prevention outcomes. Set false for eval/selftest probes so synthetic cases never inflate ROI.")
|
|
3399
3461
|
};
|
|
3400
3462
|
function tokenizeDiffForLiteral(diff) {
|
|
3401
3463
|
const lines = diff.split("\n");
|
|
@@ -3588,7 +3650,9 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3588
3650
|
}).slice(0, input.limit);
|
|
3589
3651
|
const isHardBlockCatch = (w) => w.reasons.includes("sensor");
|
|
3590
3652
|
const strongCatches = warnings.filter(isHardBlockCatch);
|
|
3591
|
-
|
|
3653
|
+
if (input.track !== false) {
|
|
3654
|
+
await recordPreventionHits(ctx.paths, strongCatches.map((w) => w.id), "anti-pattern");
|
|
3655
|
+
}
|
|
3592
3656
|
return {
|
|
3593
3657
|
scanned: negative.length,
|
|
3594
3658
|
warnings
|
|
@@ -4260,7 +4324,7 @@ async function currentAssessment(ctx) {
|
|
|
4260
4324
|
return assessBootstrapState2({
|
|
4261
4325
|
projectContextRaw,
|
|
4262
4326
|
memories,
|
|
4263
|
-
codeFiles: codeMap ? Object.keys(codeMap.files) : [],
|
|
4327
|
+
codeFiles: codeMap ? Object.keys(codeMap.files).filter((file) => existsSync29(path14.join(ctx.paths.root, file))) : [],
|
|
4264
4328
|
existingModules
|
|
4265
4329
|
});
|
|
4266
4330
|
}
|
|
@@ -4317,13 +4381,14 @@ Main code areas detected: ${areas}
|
|
|
4317
4381
|
}
|
|
4318
4382
|
var PostTaskArgsSchema = {
|
|
4319
4383
|
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
4320
|
-
files_touched: z35.
|
|
4384
|
+
files_touched: z35.string().optional().describe("Files you created or modified during the task, as CSV or a JSON array string")
|
|
4321
4385
|
};
|
|
4322
4386
|
function postTaskPrompt(args, ctx) {
|
|
4323
4387
|
const taskLine = args.task_summary ? `
|
|
4324
4388
|
Task just completed: **${args.task_summary}**` : "";
|
|
4325
|
-
const
|
|
4326
|
-
|
|
4389
|
+
const filesTouched = parsePromptFilesTouched(args.files_touched);
|
|
4390
|
+
const filesLine = filesTouched.length > 0 ? `
|
|
4391
|
+
Files touched: ${filesTouched.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
4327
4392
|
const text = `You have just finished a task. Before closing this session, take 60 seconds to capture what you learned.
|
|
4328
4393
|
${taskLine}${filesLine}
|
|
4329
4394
|
|
|
@@ -4412,6 +4477,20 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
4412
4477
|
]
|
|
4413
4478
|
};
|
|
4414
4479
|
}
|
|
4480
|
+
function parsePromptFilesTouched(input) {
|
|
4481
|
+
const raw = input?.trim();
|
|
4482
|
+
if (!raw) return [];
|
|
4483
|
+
if (raw.startsWith("[")) {
|
|
4484
|
+
try {
|
|
4485
|
+
const parsed = JSON.parse(raw);
|
|
4486
|
+
if (Array.isArray(parsed)) {
|
|
4487
|
+
return parsed.filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
|
|
4488
|
+
}
|
|
4489
|
+
} catch {
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
return raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
4493
|
+
}
|
|
4415
4494
|
var ImportDocsArgsSchema = {
|
|
4416
4495
|
content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
4417
4496
|
source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
@@ -4479,7 +4558,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4479
4558
|
};
|
|
4480
4559
|
}
|
|
4481
4560
|
var SERVER_NAME = "hivelore";
|
|
4482
|
-
var SERVER_VERSION = "0.
|
|
4561
|
+
var SERVER_VERSION = "0.44.0";
|
|
4483
4562
|
function jsonResult(data) {
|
|
4484
4563
|
return {
|
|
4485
4564
|
content: [
|
|
@@ -5418,11 +5497,22 @@ function printHaiveMcpVersion() {
|
|
|
5418
5497
|
}
|
|
5419
5498
|
async function runHaiveMcpStdio(options) {
|
|
5420
5499
|
const { server, context } = createHaiveServer({ root: options.root, env: process.env });
|
|
5500
|
+
await writeMcpRuntimeMarker(context).catch(() => {
|
|
5501
|
+
});
|
|
5421
5502
|
console.error(
|
|
5422
5503
|
`[haive-mcp] starting server v${SERVER_VERSION} (project root: ${context.paths.root})`
|
|
5423
5504
|
);
|
|
5424
5505
|
await server.connect(new StdioServerTransport());
|
|
5425
5506
|
}
|
|
5507
|
+
async function writeMcpRuntimeMarker(context) {
|
|
5508
|
+
await mkdir8(context.paths.runtimeDir, { recursive: true });
|
|
5509
|
+
await writeFile15(path15.join(context.paths.runtimeDir, "mcp-server.json"), JSON.stringify({
|
|
5510
|
+
version: SERVER_VERSION,
|
|
5511
|
+
pid: process.pid,
|
|
5512
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5513
|
+
command: "hivelore mcp --stdio"
|
|
5514
|
+
}, null, 2), "utf8");
|
|
5515
|
+
}
|
|
5426
5516
|
|
|
5427
5517
|
export {
|
|
5428
5518
|
astEngineAvailable,
|
|
@@ -5457,6 +5547,7 @@ export {
|
|
|
5457
5547
|
createHaiveServer,
|
|
5458
5548
|
parseMcpCliArgs,
|
|
5459
5549
|
printHaiveMcpVersion,
|
|
5460
|
-
runHaiveMcpStdio
|
|
5550
|
+
runHaiveMcpStdio,
|
|
5551
|
+
writeMcpRuntimeMarker
|
|
5461
5552
|
};
|
|
5462
|
-
//# sourceMappingURL=chunk-
|
|
5553
|
+
//# sourceMappingURL=chunk-6KRXMDLC.js.map
|