@hivelore/mcp 0.42.1 → 0.43.2
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 +96 -37
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +23 -6
- package/dist/server.js +96 -37
- package/dist/server.js.map +1 -1
- package/package.json +8 -4
package/dist/server.d.ts
CHANGED
|
@@ -192,6 +192,8 @@ declare const GetBriefingZod: z.ZodObject<{
|
|
|
192
192
|
semantic: z.ZodDefault<z.ZodBoolean>;
|
|
193
193
|
include_stale: z.ZodDefault<z.ZodBoolean>;
|
|
194
194
|
track: z.ZodDefault<z.ZodBoolean>;
|
|
195
|
+
memory_scopes: z.ZodOptional<z.ZodArray<z.ZodEnum<["personal", "team", "module", "shared"]>, "many">>;
|
|
196
|
+
deterministic: z.ZodOptional<z.ZodBoolean>;
|
|
195
197
|
format: z.ZodDefault<z.ZodEnum<["full", "compact", "actions"]>>;
|
|
196
198
|
symbols: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
197
199
|
min_semantic_score: z.ZodDefault<z.ZodNumber>;
|
|
@@ -210,6 +212,8 @@ declare const GetBriefingZod: z.ZodObject<{
|
|
|
210
212
|
min_semantic_score: number;
|
|
211
213
|
task?: string | undefined;
|
|
212
214
|
dedupe_project_context?: boolean | undefined;
|
|
215
|
+
memory_scopes?: ("personal" | "team" | "module" | "shared")[] | undefined;
|
|
216
|
+
deterministic?: boolean | undefined;
|
|
213
217
|
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
214
218
|
}, {
|
|
215
219
|
symbols?: string[] | undefined;
|
|
@@ -224,6 +228,8 @@ declare const GetBriefingZod: z.ZodObject<{
|
|
|
224
228
|
semantic?: boolean | undefined;
|
|
225
229
|
include_stale?: boolean | undefined;
|
|
226
230
|
track?: boolean | undefined;
|
|
231
|
+
memory_scopes?: ("personal" | "team" | "module" | "shared")[] | undefined;
|
|
232
|
+
deterministic?: boolean | undefined;
|
|
227
233
|
min_semantic_score?: number | undefined;
|
|
228
234
|
budget_preset?: "quick" | "balanced" | "deep" | undefined;
|
|
229
235
|
}>;
|
|
@@ -509,6 +515,8 @@ declare const ProposeSensorInputSchema: {
|
|
|
509
515
|
memory_id: z.ZodString;
|
|
510
516
|
kind: z.ZodDefault<z.ZodEnum<["regex", "ast", "shell", "test"]>>;
|
|
511
517
|
pattern: z.ZodOptional<z.ZodString>;
|
|
518
|
+
rule: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
519
|
+
language: z.ZodOptional<z.ZodString>;
|
|
512
520
|
command: z.ZodOptional<z.ZodString>;
|
|
513
521
|
timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
514
522
|
absent: z.ZodOptional<z.ZodString>;
|
|
@@ -561,22 +569,25 @@ interface AstScanResult {
|
|
|
561
569
|
matches: AstMatch[];
|
|
562
570
|
detail?: string;
|
|
563
571
|
}
|
|
572
|
+
type AstRule = Record<string, unknown>;
|
|
564
573
|
declare function astEngineAvailable(): Promise<boolean>;
|
|
565
574
|
/** Map a file extension to a built-in ast-grep language. Unknown → null (unsupported, warn only). */
|
|
566
|
-
declare function astLangForPath(filePath: string):
|
|
575
|
+
declare function astLangForPath(filePath: string, explicitLanguage?: string): string | null;
|
|
567
576
|
/**
|
|
568
577
|
* Run one AST pattern (with optional `absent` sub-pattern) over a file's FULL content.
|
|
569
578
|
* A match is suppressed when `absent` matches INSIDE the matched node — the structural version of
|
|
570
579
|
* the regex `absent` window: the required companion lives in the call's own arguments.
|
|
571
580
|
*/
|
|
572
|
-
declare function runAstPattern(content: string, filePath: string, pattern
|
|
581
|
+
declare function runAstPattern(content: string, filePath: string, pattern?: string, absent?: string, rule?: AstRule, language?: string): Promise<AstScanResult>;
|
|
573
582
|
/**
|
|
574
583
|
* Gate-side evaluation: matches on the full content, FIRES only when a match intersects the added
|
|
575
584
|
* lines (introduction, not mere presence). `addedLines` empty/undefined = validation mode (any
|
|
576
585
|
* match counts — used for silent-on-current / fires-on-bad checks).
|
|
577
586
|
*/
|
|
578
587
|
declare function runAstSensorOnContent(input: {
|
|
579
|
-
pattern
|
|
588
|
+
pattern?: string;
|
|
589
|
+
rule?: AstRule;
|
|
590
|
+
language?: string;
|
|
580
591
|
absent?: string;
|
|
581
592
|
content: string;
|
|
582
593
|
filePath: string;
|
|
@@ -593,8 +604,10 @@ declare const MemTriedInputSchema: {
|
|
|
593
604
|
paths: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
594
605
|
author: z.ZodOptional<z.ZodString>;
|
|
595
606
|
sensor: z.ZodOptional<z.ZodObject<{
|
|
596
|
-
kind: z.ZodDefault<z.ZodEnum<["regex", "shell", "test"]>>;
|
|
607
|
+
kind: z.ZodDefault<z.ZodEnum<["regex", "ast", "shell", "test"]>>;
|
|
597
608
|
pattern: z.ZodOptional<z.ZodString>;
|
|
609
|
+
rule: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
610
|
+
language: z.ZodOptional<z.ZodString>;
|
|
598
611
|
command: z.ZodOptional<z.ZodString>;
|
|
599
612
|
timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
600
613
|
absent: z.ZodOptional<z.ZodString>;
|
|
@@ -604,10 +617,12 @@ declare const MemTriedInputSchema: {
|
|
|
604
617
|
red_ref: z.ZodOptional<z.ZodString>;
|
|
605
618
|
bad_example: z.ZodOptional<z.ZodString>;
|
|
606
619
|
}, "strip", z.ZodTypeAny, {
|
|
607
|
-
kind: "regex" | "shell" | "test";
|
|
620
|
+
kind: "regex" | "ast" | "shell" | "test";
|
|
608
621
|
severity: "warn" | "block";
|
|
609
622
|
pattern?: string | undefined;
|
|
610
623
|
message?: string | undefined;
|
|
624
|
+
rule?: Record<string, unknown> | undefined;
|
|
625
|
+
language?: string | undefined;
|
|
611
626
|
command?: string | undefined;
|
|
612
627
|
timeout_ms?: number | undefined;
|
|
613
628
|
absent?: string | undefined;
|
|
@@ -617,7 +632,9 @@ declare const MemTriedInputSchema: {
|
|
|
617
632
|
}, {
|
|
618
633
|
pattern?: string | undefined;
|
|
619
634
|
message?: string | undefined;
|
|
620
|
-
kind?: "regex" | "shell" | "test" | undefined;
|
|
635
|
+
kind?: "regex" | "ast" | "shell" | "test" | undefined;
|
|
636
|
+
rule?: Record<string, unknown> | undefined;
|
|
637
|
+
language?: string | undefined;
|
|
621
638
|
command?: string | undefined;
|
|
622
639
|
timeout_ms?: number | undefined;
|
|
623
640
|
absent?: string | undefined;
|
package/dist/server.js
CHANGED
|
@@ -1132,7 +1132,7 @@ import {
|
|
|
1132
1132
|
import { z as z16 } from "zod";
|
|
1133
1133
|
|
|
1134
1134
|
// src/tools/propose-sensor.ts
|
|
1135
|
-
import {
|
|
1135
|
+
import { execFileSync } from "child_process";
|
|
1136
1136
|
import { readFile as readFile3, writeFile as writeFile8 } from "fs/promises";
|
|
1137
1137
|
import { existsSync as existsSync15, rmSync, symlinkSync } from "fs";
|
|
1138
1138
|
import os from "os";
|
|
@@ -1152,10 +1152,32 @@ import { z as z15 } from "zod";
|
|
|
1152
1152
|
// src/ast-sensors.ts
|
|
1153
1153
|
import path5 from "path";
|
|
1154
1154
|
var cachedEngine;
|
|
1155
|
+
var registeredDynamicLanguages = /* @__PURE__ */ new Set();
|
|
1156
|
+
async function loadDynamicLanguages(engine) {
|
|
1157
|
+
const registrations = {};
|
|
1158
|
+
const candidates = [
|
|
1159
|
+
["python", () => import("@ast-grep/lang-python")],
|
|
1160
|
+
["go", () => import("@ast-grep/lang-go")],
|
|
1161
|
+
["rust", () => import("@ast-grep/lang-rust")],
|
|
1162
|
+
["java", () => import("@ast-grep/lang-java")]
|
|
1163
|
+
];
|
|
1164
|
+
for (const [name, load] of candidates) {
|
|
1165
|
+
try {
|
|
1166
|
+
const mod = await load();
|
|
1167
|
+
registrations[name] = mod.default;
|
|
1168
|
+
registeredDynamicLanguages.add(name);
|
|
1169
|
+
} catch {
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (Object.keys(registrations).length > 0) {
|
|
1173
|
+
engine.registerDynamicLanguage(registrations);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1155
1176
|
async function loadAstEngine() {
|
|
1156
1177
|
if (cachedEngine !== void 0) return cachedEngine;
|
|
1157
1178
|
try {
|
|
1158
1179
|
cachedEngine = await import("@ast-grep/napi");
|
|
1180
|
+
await loadDynamicLanguages(cachedEngine);
|
|
1159
1181
|
} catch {
|
|
1160
1182
|
cachedEngine = null;
|
|
1161
1183
|
}
|
|
@@ -1164,11 +1186,22 @@ async function loadAstEngine() {
|
|
|
1164
1186
|
async function astEngineAvailable() {
|
|
1165
1187
|
return await loadAstEngine() !== null;
|
|
1166
1188
|
}
|
|
1167
|
-
function astLangForPath(filePath) {
|
|
1189
|
+
function astLangForPath(filePath, explicitLanguage) {
|
|
1190
|
+
if (explicitLanguage) {
|
|
1191
|
+
const normalized = explicitLanguage.toLowerCase();
|
|
1192
|
+
if (["typescript", "tsx", "javascript", "html", "css"].includes(normalized)) {
|
|
1193
|
+
return normalized === "typescript" ? "TypeScript" : normalized === "tsx" ? "Tsx" : normalized === "javascript" ? "JavaScript" : normalized[0].toUpperCase() + normalized.slice(1);
|
|
1194
|
+
}
|
|
1195
|
+
return registeredDynamicLanguages.has(normalized) ? normalized : null;
|
|
1196
|
+
}
|
|
1168
1197
|
const ext = path5.extname(filePath).toLowerCase();
|
|
1169
1198
|
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "TypeScript";
|
|
1170
1199
|
if (ext === ".tsx") return "Tsx";
|
|
1171
1200
|
if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") return "JavaScript";
|
|
1201
|
+
if (ext === ".py" && registeredDynamicLanguages.has("python")) return "python";
|
|
1202
|
+
if (ext === ".go" && registeredDynamicLanguages.has("go")) return "go";
|
|
1203
|
+
if (ext === ".rs" && registeredDynamicLanguages.has("rust")) return "rust";
|
|
1204
|
+
if (ext === ".java" && registeredDynamicLanguages.has("java")) return "java";
|
|
1172
1205
|
return null;
|
|
1173
1206
|
}
|
|
1174
1207
|
function absentPresentInNode(node, absent) {
|
|
@@ -1183,12 +1216,12 @@ function absentPresentInNode(node, absent) {
|
|
|
1183
1216
|
return text.includes(absent);
|
|
1184
1217
|
}
|
|
1185
1218
|
}
|
|
1186
|
-
async function runAstPattern(content, filePath, pattern, absent) {
|
|
1219
|
+
async function runAstPattern(content, filePath, pattern, absent, rule, language) {
|
|
1187
1220
|
const engine = await loadAstEngine();
|
|
1188
1221
|
if (!engine) return { status: "engine-missing", matches: [] };
|
|
1189
|
-
const langName = astLangForPath(filePath);
|
|
1222
|
+
const langName = astLangForPath(filePath, language);
|
|
1190
1223
|
if (!langName) return { status: "unsupported-language", matches: [] };
|
|
1191
|
-
const lang = engine.Lang[langName];
|
|
1224
|
+
const lang = langName in engine.Lang ? engine.Lang[langName] : langName;
|
|
1192
1225
|
let root;
|
|
1193
1226
|
try {
|
|
1194
1227
|
root = engine.parse(lang, content).root();
|
|
@@ -1197,7 +1230,9 @@ async function runAstPattern(content, filePath, pattern, absent) {
|
|
|
1197
1230
|
}
|
|
1198
1231
|
let nodes;
|
|
1199
1232
|
try {
|
|
1200
|
-
|
|
1233
|
+
const matcher = rule ? { rule: pattern ? { all: [{ pattern }, rule] } : rule } : pattern;
|
|
1234
|
+
if (!matcher) return { status: "invalid-pattern", matches: [], detail: "missing pattern/rule" };
|
|
1235
|
+
nodes = root.findAll(matcher);
|
|
1201
1236
|
} catch (err) {
|
|
1202
1237
|
return { status: "invalid-pattern", matches: [], detail: String(err).slice(0, 200) };
|
|
1203
1238
|
}
|
|
@@ -1214,7 +1249,7 @@ async function runAstPattern(content, filePath, pattern, absent) {
|
|
|
1214
1249
|
return { status: "ok", matches };
|
|
1215
1250
|
}
|
|
1216
1251
|
async function runAstSensorOnContent(input) {
|
|
1217
|
-
const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent);
|
|
1252
|
+
const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent, input.rule, input.language);
|
|
1218
1253
|
if (scan.status !== "ok" || !input.addedLines || input.addedLines.size === 0) return scan;
|
|
1219
1254
|
const added = input.addedLines;
|
|
1220
1255
|
return {
|
|
@@ -1234,7 +1269,9 @@ var ProposeSensorInputSchema = {
|
|
|
1234
1269
|
kind: z15.enum(["regex", "ast", "shell", "test"]).default("regex").describe(
|
|
1235
1270
|
"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."
|
|
1236
1271
|
),
|
|
1237
|
-
pattern: z15.string().optional().describe("kind=regex: regex matching the
|
|
1272
|
+
pattern: z15.string().optional().describe("kind=regex: regex matching the faulty usage; kind=ast: optional structural pattern (may be combined with `rule`)."),
|
|
1273
|
+
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."),
|
|
1274
|
+
language: z15.string().optional().describe("kind=ast: explicit built-in/dynamic language name for non-standard file extensions."),
|
|
1238
1275
|
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."),
|
|
1239
1276
|
timeout_ms: z15.number().int().positive().optional().describe("kind=shell|test: max runtime before the executor kills the command (default 120000)."),
|
|
1240
1277
|
absent: z15.string().optional().describe(
|
|
@@ -1265,7 +1302,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
1265
1302
|
const targets = [];
|
|
1266
1303
|
for (const rel of relPaths) {
|
|
1267
1304
|
try {
|
|
1268
|
-
const content =
|
|
1305
|
+
const content = execFileSync("git", ["show", `HEAD:./${rel}`], {
|
|
1269
1306
|
cwd: root,
|
|
1270
1307
|
encoding: "utf8",
|
|
1271
1308
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -1286,7 +1323,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
1286
1323
|
}
|
|
1287
1324
|
function runCommandForValidation(command, root, timeoutMs = 12e4) {
|
|
1288
1325
|
try {
|
|
1289
|
-
|
|
1326
|
+
execFileSync("bash", ["-c", command], {
|
|
1290
1327
|
cwd: root,
|
|
1291
1328
|
timeout: timeoutMs,
|
|
1292
1329
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -1311,7 +1348,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
|
|
|
1311
1348
|
let added = false;
|
|
1312
1349
|
try {
|
|
1313
1350
|
try {
|
|
1314
|
-
|
|
1351
|
+
execFileSync("git", ["worktree", "add", "--detach", worktree, redRef], {
|
|
1315
1352
|
cwd: root,
|
|
1316
1353
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1317
1354
|
timeout: 6e4
|
|
@@ -1338,7 +1375,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
|
|
|
1338
1375
|
} finally {
|
|
1339
1376
|
if (added) {
|
|
1340
1377
|
try {
|
|
1341
|
-
|
|
1378
|
+
execFileSync("git", ["worktree", "remove", "--force", worktree], { cwd: root, stdio: "ignore", timeout: 6e4 });
|
|
1342
1379
|
} catch {
|
|
1343
1380
|
try {
|
|
1344
1381
|
rmSync(worktree, { recursive: true, force: true });
|
|
@@ -1378,13 +1415,13 @@ async function proposeSensor(input, ctx) {
|
|
|
1378
1415
|
};
|
|
1379
1416
|
}
|
|
1380
1417
|
} else if (kind === "ast") {
|
|
1381
|
-
if (!input.pattern?.trim()) {
|
|
1418
|
+
if (!input.pattern?.trim() && !input.rule) {
|
|
1382
1419
|
return {
|
|
1383
1420
|
accepted: false,
|
|
1384
1421
|
memory_id: input.memory_id,
|
|
1385
1422
|
severity: input.severity,
|
|
1386
1423
|
reason: "invalid-pattern",
|
|
1387
|
-
guidance: "kind=ast requires a `pattern`
|
|
1424
|
+
guidance: "kind=ast requires a structural `pattern` or a full ast-grep `rule` object.",
|
|
1388
1425
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
|
|
1389
1426
|
};
|
|
1390
1427
|
}
|
|
@@ -1405,7 +1442,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1405
1442
|
}
|
|
1406
1443
|
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}.` : "";
|
|
1407
1444
|
if (kind === "ast") {
|
|
1408
|
-
const pattern = input.pattern
|
|
1445
|
+
const pattern = input.pattern?.trim();
|
|
1409
1446
|
if (!await astEngineAvailable() && input.severity === "block") {
|
|
1410
1447
|
return {
|
|
1411
1448
|
accepted: false,
|
|
@@ -1416,7 +1453,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1416
1453
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
|
|
1417
1454
|
};
|
|
1418
1455
|
}
|
|
1419
|
-
const brittleAst = sensorPatternBrittleness(pattern);
|
|
1456
|
+
const brittleAst = pattern ? sensorPatternBrittleness(pattern) : null;
|
|
1420
1457
|
if (brittleAst && input.severity === "block") {
|
|
1421
1458
|
return {
|
|
1422
1459
|
accepted: false,
|
|
@@ -1431,7 +1468,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1431
1468
|
const currentTargetsAst = await readPresumedCorrectTargets(ctx.paths.root, anchorPathsAst);
|
|
1432
1469
|
const firedOnAst = [];
|
|
1433
1470
|
for (const target of currentTargetsAst) {
|
|
1434
|
-
const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: target.content, filePath: target.path });
|
|
1471
|
+
const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: target.content, filePath: target.path });
|
|
1435
1472
|
if (scan.status === "invalid-pattern") {
|
|
1436
1473
|
return {
|
|
1437
1474
|
accepted: false,
|
|
@@ -1460,10 +1497,10 @@ async function proposeSensor(input, ctx) {
|
|
|
1460
1497
|
];
|
|
1461
1498
|
let firesOnBadAst = null;
|
|
1462
1499
|
if (badExamplesAst.length > 0 && await astEngineAvailable()) {
|
|
1463
|
-
const exampleLang = anchorPathsAst.find((p) => astLangForPath(p) !== null) ?? "example.tsx";
|
|
1500
|
+
const exampleLang = anchorPathsAst.find((p) => astLangForPath(p, input.language) !== null) ?? "example.tsx";
|
|
1464
1501
|
firesOnBadAst = false;
|
|
1465
1502
|
for (const example of badExamplesAst) {
|
|
1466
|
-
const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: example, filePath: exampleLang });
|
|
1503
|
+
const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: example, filePath: exampleLang });
|
|
1467
1504
|
if (scan.status === "ok" && scan.matches.length > 0) {
|
|
1468
1505
|
firesOnBadAst = true;
|
|
1469
1506
|
break;
|
|
@@ -1482,10 +1519,12 @@ async function proposeSensor(input, ctx) {
|
|
|
1482
1519
|
}
|
|
1483
1520
|
const sensorAst = {
|
|
1484
1521
|
kind: "ast",
|
|
1485
|
-
pattern,
|
|
1522
|
+
...pattern ? { pattern } : {},
|
|
1523
|
+
...input.rule ? { rule: input.rule } : {},
|
|
1524
|
+
...input.language ? { language: input.language } : {},
|
|
1486
1525
|
...input.absent ? { absent: input.absent } : {},
|
|
1487
1526
|
paths: anchorPathsAst,
|
|
1488
|
-
message: input.message?.trim() || deriveMessage(found.memory.body, pattern, input.absent),
|
|
1527
|
+
message: input.message?.trim() || deriveMessage(found.memory.body, pattern ?? "AST rule", input.absent),
|
|
1489
1528
|
...input.incident?.trim() ? { incident: input.incident.trim() } : {},
|
|
1490
1529
|
severity: input.severity,
|
|
1491
1530
|
autogen: false,
|
|
@@ -1548,6 +1587,16 @@ ${verdictCmd.detail}`,
|
|
|
1548
1587
|
self_check: { silent_on_current: false, fires_on_bad: null, fired_on: anchorPathsCmd }
|
|
1549
1588
|
};
|
|
1550
1589
|
}
|
|
1590
|
+
if (input.severity === "block" && !input.red_ref?.trim()) {
|
|
1591
|
+
return {
|
|
1592
|
+
accepted: false,
|
|
1593
|
+
memory_id: input.memory_id,
|
|
1594
|
+
severity: input.severity,
|
|
1595
|
+
reason: "red-required",
|
|
1596
|
+
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.",
|
|
1597
|
+
self_check: { silent_on_current: true, fires_on_bad: null, fired_on: [] }
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1551
1600
|
const sensorCmd = {
|
|
1552
1601
|
kind,
|
|
1553
1602
|
command: input.command.trim(),
|
|
@@ -1569,7 +1618,7 @@ ${verdictCmd.detail}`,
|
|
|
1569
1618
|
accepted: true,
|
|
1570
1619
|
memory_id: input.memory_id,
|
|
1571
1620
|
severity: input.severity,
|
|
1572
|
-
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." : "
|
|
1621
|
+
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,
|
|
1573
1622
|
self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: redProven ? true : null, fired_on: [] },
|
|
1574
1623
|
file_path: found.filePath
|
|
1575
1624
|
};
|
|
@@ -1638,8 +1687,10 @@ var MemTriedInputSchema = {
|
|
|
1638
1687
|
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
1639
1688
|
author: z16.string().optional().describe("Author handle or email"),
|
|
1640
1689
|
sensor: z16.object({
|
|
1641
|
-
kind: z16.enum(["regex", "shell", "test"]).default("regex").describe("regex pattern, or a shell/test
|
|
1642
|
-
pattern: z16.string().optional().describe("kind=regex:
|
|
1690
|
+
kind: z16.enum(["regex", "ast", "shell", "test"]).default("regex").describe("regex/AST pattern, or a shell/test command"),
|
|
1691
|
+
pattern: z16.string().optional().describe("kind=regex|ast: pattern matching the faulty usage"),
|
|
1692
|
+
rule: z16.record(z16.unknown()).optional().describe("kind=ast: full ast-grep Rule object"),
|
|
1693
|
+
language: z16.string().optional().describe("kind=ast: explicit built-in/dynamic language"),
|
|
1643
1694
|
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)"),
|
|
1644
1695
|
timeout_ms: z16.number().int().positive().optional().describe("kind=shell|test: max runtime (default 120000)"),
|
|
1645
1696
|
absent: z16.string().optional().describe("kind=regex: regex marking CORRECT usage nearby \u2014 excludes it from firing"),
|
|
@@ -1689,6 +1740,8 @@ async function memTried(input, ctx) {
|
|
|
1689
1740
|
memory_id: frontmatter.id,
|
|
1690
1741
|
kind: input.sensor.kind ?? "regex",
|
|
1691
1742
|
pattern: input.sensor.pattern,
|
|
1743
|
+
rule: input.sensor.rule,
|
|
1744
|
+
language: input.sensor.language,
|
|
1692
1745
|
command: input.sensor.command,
|
|
1693
1746
|
timeout_ms: input.sensor.timeout_ms,
|
|
1694
1747
|
absent: input.sensor.absent,
|
|
@@ -1991,7 +2044,7 @@ import {
|
|
|
1991
2044
|
import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
|
|
1992
2045
|
import { existsSync as existsSync19 } from "fs";
|
|
1993
2046
|
import path10 from "path";
|
|
1994
|
-
import { execSync
|
|
2047
|
+
import { execSync } from "child_process";
|
|
1995
2048
|
function pendingDistillPath(ctx) {
|
|
1996
2049
|
return path10.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
|
|
1997
2050
|
}
|
|
@@ -2034,7 +2087,7 @@ var SessionTracker = class {
|
|
|
2034
2087
|
}
|
|
2035
2088
|
let gitDiff;
|
|
2036
2089
|
try {
|
|
2037
|
-
const raw =
|
|
2090
|
+
const raw = execSync("git diff HEAD", {
|
|
2038
2091
|
cwd: this.ctx.paths.root,
|
|
2039
2092
|
timeout: 5e3,
|
|
2040
2093
|
encoding: "utf8",
|
|
@@ -2066,7 +2119,7 @@ var SessionTracker = class {
|
|
|
2066
2119
|
const triedThreads = this.events.filter((e) => e.tool === "mem_tried").map((e) => e.summary ?? "").filter(Boolean);
|
|
2067
2120
|
let diffStat;
|
|
2068
2121
|
try {
|
|
2069
|
-
diffStat =
|
|
2122
|
+
diffStat = execSync("git diff --stat HEAD", {
|
|
2070
2123
|
cwd: this.ctx.paths.root,
|
|
2071
2124
|
timeout: 5e3,
|
|
2072
2125
|
encoding: "utf8",
|
|
@@ -2478,6 +2531,8 @@ var GetBriefingInputSchema = {
|
|
|
2478
2531
|
),
|
|
2479
2532
|
include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
2480
2533
|
track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
2534
|
+
memory_scopes: z20.array(z20.enum(["personal", "team", "module", "shared"])).optional().describe("Restrict the candidate corpus to selected scopes. Omit to include every scope."),
|
|
2535
|
+
deterministic: z20.boolean().optional().describe("Ignore machine-local usage/impact signals so repeated evaluations rank the shared corpus reproducibly."),
|
|
2481
2536
|
format: z20.enum(["full", "compact", "actions"]).default("full").describe(
|
|
2482
2537
|
"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."
|
|
2483
2538
|
),
|
|
@@ -2506,9 +2561,11 @@ async function getBriefing(input, ctx) {
|
|
|
2506
2561
|
let searchMode = "literal";
|
|
2507
2562
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
2508
2563
|
let byId = /* @__PURE__ */ new Map();
|
|
2564
|
+
const allowedScopes = input.memory_scopes ? new Set(input.memory_scopes) : null;
|
|
2565
|
+
const scopeAllowed = (loaded) => allowedScopes === null || allowedScopes.has(loaded.memory.frontmatter.scope);
|
|
2509
2566
|
let lastSession;
|
|
2510
2567
|
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2511
|
-
const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2568
|
+
const allLoaded = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
|
|
2512
2569
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
2513
2570
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2514
2571
|
);
|
|
@@ -2541,10 +2598,11 @@ async function getBriefing(input, ctx) {
|
|
|
2541
2598
|
if (memoryHasExcludedTag(memory.frontmatter, excludeTags)) return false;
|
|
2542
2599
|
return true;
|
|
2543
2600
|
});
|
|
2544
|
-
usage = await loadUsageIndex8(ctx.paths);
|
|
2601
|
+
usage = input.deterministic ? { version: 1, updated_at: "", by_id: {} } : await loadUsageIndex8(ctx.paths);
|
|
2545
2602
|
byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
2546
|
-
const
|
|
2547
|
-
|
|
2603
|
+
const semanticEnabled = input.semantic && input.deterministic !== true;
|
|
2604
|
+
const semanticHits = input.task && semanticEnabled ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
2605
|
+
if (input.task && semanticEnabled) {
|
|
2548
2606
|
searchMode = semanticHits ? "semantic" : "literal_fallback";
|
|
2549
2607
|
}
|
|
2550
2608
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -2880,7 +2938,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2880
2938
|
}
|
|
2881
2939
|
}
|
|
2882
2940
|
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2883
|
-
const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2941
|
+
const allMems = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
|
|
2884
2942
|
for (const { memory } of allMems) {
|
|
2885
2943
|
const fm = memory.frontmatter;
|
|
2886
2944
|
if (!fm.requires_human_approval) continue;
|
|
@@ -2937,7 +2995,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2937
2995
|
pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
|
|
2938
2996
|
} catch {
|
|
2939
2997
|
}
|
|
2940
|
-
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
|
|
2998
|
+
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed) : [];
|
|
2941
2999
|
const cmForBootstrap = await loadCodeMap(ctx.paths);
|
|
2942
3000
|
let existingModules = [];
|
|
2943
3001
|
try {
|
|
@@ -2948,7 +3006,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2948
3006
|
const bootstrap = assessBootstrapState({
|
|
2949
3007
|
projectContextRaw: pcRaw,
|
|
2950
3008
|
memories: allForBootstrap,
|
|
2951
|
-
codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files) : [],
|
|
3009
|
+
codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files).filter((file) => existsSync22(path13.join(ctx.paths.root, file))) : [],
|
|
2952
3010
|
existingModules
|
|
2953
3011
|
});
|
|
2954
3012
|
if (bootstrap.state !== "ready" && bootstrap.metrics.mainAreas > 0) {
|
|
@@ -3139,7 +3197,7 @@ function oneLine(value) {
|
|
|
3139
3197
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
3140
3198
|
}
|
|
3141
3199
|
function serverVersion() {
|
|
3142
|
-
return true ? "0.
|
|
3200
|
+
return true ? "0.43.2" : "dev";
|
|
3143
3201
|
}
|
|
3144
3202
|
|
|
3145
3203
|
// src/tools/code-map.ts
|
|
@@ -4312,6 +4370,7 @@ ${template}\`\`\`
|
|
|
4312
4370
|
// src/prompts/bootstrap-repo.ts
|
|
4313
4371
|
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
4314
4372
|
import { existsSync as existsSync29 } from "fs";
|
|
4373
|
+
import path14 from "path";
|
|
4315
4374
|
import {
|
|
4316
4375
|
assessBootstrapState as assessBootstrapState2,
|
|
4317
4376
|
loadCodeMap as loadCodeMap4,
|
|
@@ -4339,7 +4398,7 @@ async function currentAssessment(ctx) {
|
|
|
4339
4398
|
return assessBootstrapState2({
|
|
4340
4399
|
projectContextRaw,
|
|
4341
4400
|
memories,
|
|
4342
|
-
codeFiles: codeMap ? Object.keys(codeMap.files) : [],
|
|
4401
|
+
codeFiles: codeMap ? Object.keys(codeMap.files).filter((file) => existsSync29(path14.join(ctx.paths.root, file))) : [],
|
|
4343
4402
|
existingModules
|
|
4344
4403
|
});
|
|
4345
4404
|
}
|
|
@@ -4567,7 +4626,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4567
4626
|
// src/server.ts
|
|
4568
4627
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
4569
4628
|
var SERVER_NAME = "hivelore";
|
|
4570
|
-
var SERVER_VERSION = "0.
|
|
4629
|
+
var SERVER_VERSION = "0.43.2";
|
|
4571
4630
|
function jsonResult(data) {
|
|
4572
4631
|
return {
|
|
4573
4632
|
content: [
|