@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 CHANGED
@@ -1134,7 +1134,7 @@ import {
1134
1134
  import { z as z16 } from "zod";
1135
1135
 
1136
1136
  // src/tools/propose-sensor.ts
1137
- import { execSync } from "child_process";
1137
+ import { execFileSync } from "child_process";
1138
1138
  import { readFile as readFile3, writeFile as writeFile8 } from "fs/promises";
1139
1139
  import { existsSync as existsSync15, rmSync, symlinkSync } from "fs";
1140
1140
  import os from "os";
@@ -1154,10 +1154,32 @@ import { z as z15 } from "zod";
1154
1154
  // src/ast-sensors.ts
1155
1155
  import path5 from "path";
1156
1156
  var cachedEngine;
1157
+ var registeredDynamicLanguages = /* @__PURE__ */ new Set();
1158
+ async function loadDynamicLanguages(engine) {
1159
+ const registrations = {};
1160
+ const candidates = [
1161
+ ["python", () => import("@ast-grep/lang-python")],
1162
+ ["go", () => import("@ast-grep/lang-go")],
1163
+ ["rust", () => import("@ast-grep/lang-rust")],
1164
+ ["java", () => import("@ast-grep/lang-java")]
1165
+ ];
1166
+ for (const [name, load] of candidates) {
1167
+ try {
1168
+ const mod = await load();
1169
+ registrations[name] = mod.default;
1170
+ registeredDynamicLanguages.add(name);
1171
+ } catch {
1172
+ }
1173
+ }
1174
+ if (Object.keys(registrations).length > 0) {
1175
+ engine.registerDynamicLanguage(registrations);
1176
+ }
1177
+ }
1157
1178
  async function loadAstEngine() {
1158
1179
  if (cachedEngine !== void 0) return cachedEngine;
1159
1180
  try {
1160
1181
  cachedEngine = await import("@ast-grep/napi");
1182
+ await loadDynamicLanguages(cachedEngine);
1161
1183
  } catch {
1162
1184
  cachedEngine = null;
1163
1185
  }
@@ -1166,11 +1188,22 @@ async function loadAstEngine() {
1166
1188
  async function astEngineAvailable() {
1167
1189
  return await loadAstEngine() !== null;
1168
1190
  }
1169
- function astLangForPath(filePath) {
1191
+ function astLangForPath(filePath, explicitLanguage) {
1192
+ if (explicitLanguage) {
1193
+ const normalized = explicitLanguage.toLowerCase();
1194
+ if (["typescript", "tsx", "javascript", "html", "css"].includes(normalized)) {
1195
+ return normalized === "typescript" ? "TypeScript" : normalized === "tsx" ? "Tsx" : normalized === "javascript" ? "JavaScript" : normalized[0].toUpperCase() + normalized.slice(1);
1196
+ }
1197
+ return registeredDynamicLanguages.has(normalized) ? normalized : null;
1198
+ }
1170
1199
  const ext = path5.extname(filePath).toLowerCase();
1171
1200
  if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "TypeScript";
1172
1201
  if (ext === ".tsx") return "Tsx";
1173
1202
  if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") return "JavaScript";
1203
+ if (ext === ".py" && registeredDynamicLanguages.has("python")) return "python";
1204
+ if (ext === ".go" && registeredDynamicLanguages.has("go")) return "go";
1205
+ if (ext === ".rs" && registeredDynamicLanguages.has("rust")) return "rust";
1206
+ if (ext === ".java" && registeredDynamicLanguages.has("java")) return "java";
1174
1207
  return null;
1175
1208
  }
1176
1209
  function absentPresentInNode(node, absent) {
@@ -1185,12 +1218,12 @@ function absentPresentInNode(node, absent) {
1185
1218
  return text.includes(absent);
1186
1219
  }
1187
1220
  }
1188
- async function runAstPattern(content, filePath, pattern, absent) {
1221
+ async function runAstPattern(content, filePath, pattern, absent, rule, language) {
1189
1222
  const engine = await loadAstEngine();
1190
1223
  if (!engine) return { status: "engine-missing", matches: [] };
1191
- const langName = astLangForPath(filePath);
1224
+ const langName = astLangForPath(filePath, language);
1192
1225
  if (!langName) return { status: "unsupported-language", matches: [] };
1193
- const lang = engine.Lang[langName];
1226
+ const lang = langName in engine.Lang ? engine.Lang[langName] : langName;
1194
1227
  let root;
1195
1228
  try {
1196
1229
  root = engine.parse(lang, content).root();
@@ -1199,7 +1232,9 @@ async function runAstPattern(content, filePath, pattern, absent) {
1199
1232
  }
1200
1233
  let nodes;
1201
1234
  try {
1202
- nodes = root.findAll(pattern);
1235
+ const matcher = rule ? { rule: pattern ? { all: [{ pattern }, rule] } : rule } : pattern;
1236
+ if (!matcher) return { status: "invalid-pattern", matches: [], detail: "missing pattern/rule" };
1237
+ nodes = root.findAll(matcher);
1203
1238
  } catch (err) {
1204
1239
  return { status: "invalid-pattern", matches: [], detail: String(err).slice(0, 200) };
1205
1240
  }
@@ -1216,7 +1251,7 @@ async function runAstPattern(content, filePath, pattern, absent) {
1216
1251
  return { status: "ok", matches };
1217
1252
  }
1218
1253
  async function runAstSensorOnContent(input) {
1219
- const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent);
1254
+ const scan = await runAstPattern(input.content, input.filePath, input.pattern, input.absent, input.rule, input.language);
1220
1255
  if (scan.status !== "ok" || !input.addedLines || input.addedLines.size === 0) return scan;
1221
1256
  const added = input.addedLines;
1222
1257
  return {
@@ -1236,7 +1271,9 @@ var ProposeSensorInputSchema = {
1236
1271
  kind: z15.enum(["regex", "ast", "shell", "test"]).default("regex").describe(
1237
1272
  "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."
1238
1273
  ),
1239
- pattern: z15.string().optional().describe("kind=regex: regex matching the FAULTY usage (the risky call/token), e.g. 'stripe\\.paymentIntents\\.create'."),
1274
+ pattern: z15.string().optional().describe("kind=regex: regex matching the faulty usage; kind=ast: optional structural pattern (may be combined with `rule`)."),
1275
+ 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."),
1276
+ language: z15.string().optional().describe("kind=ast: explicit built-in/dynamic language name for non-standard file extensions."),
1240
1277
  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."),
1241
1278
  timeout_ms: z15.number().int().positive().optional().describe("kind=shell|test: max runtime before the executor kills the command (default 120000)."),
1242
1279
  absent: z15.string().optional().describe(
@@ -1267,7 +1304,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
1267
1304
  const targets = [];
1268
1305
  for (const rel of relPaths) {
1269
1306
  try {
1270
- const content = execSync(`git show ${JSON.stringify(`HEAD:./${rel}`)}`, {
1307
+ const content = execFileSync("git", ["show", `HEAD:./${rel}`], {
1271
1308
  cwd: root,
1272
1309
  encoding: "utf8",
1273
1310
  maxBuffer: 10 * 1024 * 1024,
@@ -1288,7 +1325,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
1288
1325
  }
1289
1326
  function runCommandForValidation(command, root, timeoutMs = 12e4) {
1290
1327
  try {
1291
- execSync(`bash -c ${JSON.stringify(command)}`, {
1328
+ execFileSync("bash", ["-c", command], {
1292
1329
  cwd: root,
1293
1330
  timeout: timeoutMs,
1294
1331
  maxBuffer: 8 * 1024 * 1024,
@@ -1313,7 +1350,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
1313
1350
  let added = false;
1314
1351
  try {
1315
1352
  try {
1316
- execSync(`git worktree add --detach ${JSON.stringify(worktree)} ${JSON.stringify(redRef)}`, {
1353
+ execFileSync("git", ["worktree", "add", "--detach", worktree, redRef], {
1317
1354
  cwd: root,
1318
1355
  stdio: ["ignore", "pipe", "pipe"],
1319
1356
  timeout: 6e4
@@ -1340,7 +1377,7 @@ function proveRedOnIncident(command, root, redRef, timeoutMs) {
1340
1377
  } finally {
1341
1378
  if (added) {
1342
1379
  try {
1343
- execSync(`git worktree remove --force ${JSON.stringify(worktree)}`, { cwd: root, stdio: "ignore", timeout: 6e4 });
1380
+ execFileSync("git", ["worktree", "remove", "--force", worktree], { cwd: root, stdio: "ignore", timeout: 6e4 });
1344
1381
  } catch {
1345
1382
  try {
1346
1383
  rmSync(worktree, { recursive: true, force: true });
@@ -1380,13 +1417,13 @@ async function proposeSensor(input, ctx) {
1380
1417
  };
1381
1418
  }
1382
1419
  } else if (kind === "ast") {
1383
- if (!input.pattern?.trim()) {
1420
+ if (!input.pattern?.trim() && !input.rule) {
1384
1421
  return {
1385
1422
  accepted: false,
1386
1423
  memory_id: input.memory_id,
1387
1424
  severity: input.severity,
1388
1425
  reason: "invalid-pattern",
1389
- guidance: "kind=ast requires a `pattern` (an ast-grep structural pattern).",
1426
+ guidance: "kind=ast requires a structural `pattern` or a full ast-grep `rule` object.",
1390
1427
  self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
1391
1428
  };
1392
1429
  }
@@ -1407,7 +1444,7 @@ async function proposeSensor(input, ctx) {
1407
1444
  }
1408
1445
  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}.` : "";
1409
1446
  if (kind === "ast") {
1410
- const pattern = input.pattern.trim();
1447
+ const pattern = input.pattern?.trim();
1411
1448
  if (!await astEngineAvailable() && input.severity === "block") {
1412
1449
  return {
1413
1450
  accepted: false,
@@ -1418,7 +1455,7 @@ async function proposeSensor(input, ctx) {
1418
1455
  self_check: { silent_on_current: false, fires_on_bad: null, fired_on: [] }
1419
1456
  };
1420
1457
  }
1421
- const brittleAst = sensorPatternBrittleness(pattern);
1458
+ const brittleAst = pattern ? sensorPatternBrittleness(pattern) : null;
1422
1459
  if (brittleAst && input.severity === "block") {
1423
1460
  return {
1424
1461
  accepted: false,
@@ -1433,7 +1470,7 @@ async function proposeSensor(input, ctx) {
1433
1470
  const currentTargetsAst = await readPresumedCorrectTargets(ctx.paths.root, anchorPathsAst);
1434
1471
  const firedOnAst = [];
1435
1472
  for (const target of currentTargetsAst) {
1436
- const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: target.content, filePath: target.path });
1473
+ const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: target.content, filePath: target.path });
1437
1474
  if (scan.status === "invalid-pattern") {
1438
1475
  return {
1439
1476
  accepted: false,
@@ -1462,10 +1499,10 @@ async function proposeSensor(input, ctx) {
1462
1499
  ];
1463
1500
  let firesOnBadAst = null;
1464
1501
  if (badExamplesAst.length > 0 && await astEngineAvailable()) {
1465
- const exampleLang = anchorPathsAst.find((p) => astLangForPath(p) !== null) ?? "example.tsx";
1502
+ const exampleLang = anchorPathsAst.find((p) => astLangForPath(p, input.language) !== null) ?? "example.tsx";
1466
1503
  firesOnBadAst = false;
1467
1504
  for (const example of badExamplesAst) {
1468
- const scan = await runAstSensorOnContent({ pattern, absent: input.absent, content: example, filePath: exampleLang });
1505
+ const scan = await runAstSensorOnContent({ pattern, rule: input.rule, language: input.language, absent: input.absent, content: example, filePath: exampleLang });
1469
1506
  if (scan.status === "ok" && scan.matches.length > 0) {
1470
1507
  firesOnBadAst = true;
1471
1508
  break;
@@ -1484,10 +1521,12 @@ async function proposeSensor(input, ctx) {
1484
1521
  }
1485
1522
  const sensorAst = {
1486
1523
  kind: "ast",
1487
- pattern,
1524
+ ...pattern ? { pattern } : {},
1525
+ ...input.rule ? { rule: input.rule } : {},
1526
+ ...input.language ? { language: input.language } : {},
1488
1527
  ...input.absent ? { absent: input.absent } : {},
1489
1528
  paths: anchorPathsAst,
1490
- message: input.message?.trim() || deriveMessage(found.memory.body, pattern, input.absent),
1529
+ message: input.message?.trim() || deriveMessage(found.memory.body, pattern ?? "AST rule", input.absent),
1491
1530
  ...input.incident?.trim() ? { incident: input.incident.trim() } : {},
1492
1531
  severity: input.severity,
1493
1532
  autogen: false,
@@ -1550,6 +1589,16 @@ ${verdictCmd.detail}`,
1550
1589
  self_check: { silent_on_current: false, fires_on_bad: null, fired_on: anchorPathsCmd }
1551
1590
  };
1552
1591
  }
1592
+ if (input.severity === "block" && !input.red_ref?.trim()) {
1593
+ return {
1594
+ accepted: false,
1595
+ memory_id: input.memory_id,
1596
+ severity: input.severity,
1597
+ reason: "red-required",
1598
+ 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.",
1599
+ self_check: { silent_on_current: true, fires_on_bad: null, fired_on: [] }
1600
+ };
1601
+ }
1553
1602
  const sensorCmd = {
1554
1603
  kind,
1555
1604
  command: input.command.trim(),
@@ -1571,7 +1620,7 @@ ${verdictCmd.detail}`,
1571
1620
  accepted: true,
1572
1621
  memory_id: input.memory_id,
1573
1622
  severity: input.severity,
1574
- 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." : " Tip: pass red_ref (the pre-fix commit) to PROVE the oracle catches the incident, not merely that it passes today.") + (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,
1623
+ 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,
1575
1624
  self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: redProven ? true : null, fired_on: [] },
1576
1625
  file_path: found.filePath
1577
1626
  };
@@ -1640,8 +1689,10 @@ var MemTriedInputSchema = {
1640
1689
  paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
1641
1690
  author: z16.string().optional().describe("Author handle or email"),
1642
1691
  sensor: z16.object({
1643
- kind: z16.enum(["regex", "shell", "test"]).default("regex").describe("regex pattern, or a shell/test COMMAND the gate executes (behaviour bridge)"),
1644
- pattern: z16.string().optional().describe("kind=regex: regex matching the FAULTY usage (added diff lines)"),
1692
+ kind: z16.enum(["regex", "ast", "shell", "test"]).default("regex").describe("regex/AST pattern, or a shell/test command"),
1693
+ pattern: z16.string().optional().describe("kind=regex|ast: pattern matching the faulty usage"),
1694
+ rule: z16.record(z16.unknown()).optional().describe("kind=ast: full ast-grep Rule object"),
1695
+ language: z16.string().optional().describe("kind=ast: explicit built-in/dynamic language"),
1645
1696
  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)"),
1646
1697
  timeout_ms: z16.number().int().positive().optional().describe("kind=shell|test: max runtime (default 120000)"),
1647
1698
  absent: z16.string().optional().describe("kind=regex: regex marking CORRECT usage nearby \u2014 excludes it from firing"),
@@ -1691,6 +1742,8 @@ async function memTried(input, ctx) {
1691
1742
  memory_id: frontmatter.id,
1692
1743
  kind: input.sensor.kind ?? "regex",
1693
1744
  pattern: input.sensor.pattern,
1745
+ rule: input.sensor.rule,
1746
+ language: input.sensor.language,
1694
1747
  command: input.sensor.command,
1695
1748
  timeout_ms: input.sensor.timeout_ms,
1696
1749
  absent: input.sensor.absent,
@@ -1985,7 +2038,7 @@ import {
1985
2038
  import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
1986
2039
  import { existsSync as existsSync19 } from "fs";
1987
2040
  import path10 from "path";
1988
- import { execSync as execSync2 } from "child_process";
2041
+ import { execSync } from "child_process";
1989
2042
  function pendingDistillPath(ctx) {
1990
2043
  return path10.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
1991
2044
  }
@@ -2028,7 +2081,7 @@ var SessionTracker = class {
2028
2081
  }
2029
2082
  let gitDiff;
2030
2083
  try {
2031
- const raw = execSync2("git diff HEAD", {
2084
+ const raw = execSync("git diff HEAD", {
2032
2085
  cwd: this.ctx.paths.root,
2033
2086
  timeout: 5e3,
2034
2087
  encoding: "utf8",
@@ -2060,7 +2113,7 @@ var SessionTracker = class {
2060
2113
  const triedThreads = this.events.filter((e) => e.tool === "mem_tried").map((e) => e.summary ?? "").filter(Boolean);
2061
2114
  let diffStat;
2062
2115
  try {
2063
- diffStat = execSync2("git diff --stat HEAD", {
2116
+ diffStat = execSync("git diff --stat HEAD", {
2064
2117
  cwd: this.ctx.paths.root,
2065
2118
  timeout: 5e3,
2066
2119
  encoding: "utf8",
@@ -2472,6 +2525,8 @@ var GetBriefingInputSchema = {
2472
2525
  ),
2473
2526
  include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
2474
2527
  track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
2528
+ memory_scopes: z20.array(z20.enum(["personal", "team", "module", "shared"])).optional().describe("Restrict the candidate corpus to selected scopes. Omit to include every scope."),
2529
+ deterministic: z20.boolean().optional().describe("Ignore machine-local usage/impact signals so repeated evaluations rank the shared corpus reproducibly."),
2475
2530
  format: z20.enum(["full", "compact", "actions"]).default("full").describe(
2476
2531
  "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."
2477
2532
  ),
@@ -2500,9 +2555,11 @@ async function getBriefing(input, ctx) {
2500
2555
  let searchMode = "literal";
2501
2556
  let usage = { version: 1, updated_at: "", by_id: {} };
2502
2557
  let byId = /* @__PURE__ */ new Map();
2558
+ const allowedScopes = input.memory_scopes ? new Set(input.memory_scopes) : null;
2559
+ const scopeAllowed = (loaded) => allowedScopes === null || allowedScopes.has(loaded.memory.frontmatter.scope);
2503
2560
  let lastSession;
2504
2561
  if (existsSync22(ctx.paths.memoriesDir)) {
2505
- const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2562
+ const allLoaded = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
2506
2563
  const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
2507
2564
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
2508
2565
  );
@@ -2535,10 +2592,11 @@ async function getBriefing(input, ctx) {
2535
2592
  if (memoryHasExcludedTag(memory.frontmatter, excludeTags)) return false;
2536
2593
  return true;
2537
2594
  });
2538
- usage = await loadUsageIndex8(ctx.paths);
2595
+ usage = input.deterministic ? { version: 1, updated_at: "", by_id: {} } : await loadUsageIndex8(ctx.paths);
2539
2596
  byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
2540
- const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
2541
- if (input.task && input.semantic) {
2597
+ const semanticEnabled = input.semantic && input.deterministic !== true;
2598
+ const semanticHits = input.task && semanticEnabled ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
2599
+ if (input.task && semanticEnabled) {
2542
2600
  searchMode = semanticHits ? "semantic" : "literal_fallback";
2543
2601
  }
2544
2602
  const seen = /* @__PURE__ */ new Map();
@@ -2874,7 +2932,7 @@ ${m.content}`).join("\n\n---\n\n"),
2874
2932
  }
2875
2933
  }
2876
2934
  if (existsSync22(ctx.paths.memoriesDir)) {
2877
- const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2935
+ const allMems = (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed);
2878
2936
  for (const { memory } of allMems) {
2879
2937
  const fm = memory.frontmatter;
2880
2938
  if (!fm.requires_human_approval) continue;
@@ -2931,7 +2989,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2931
2989
  pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
2932
2990
  } catch {
2933
2991
  }
2934
- const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
2992
+ const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? (await loadMemoriesFromDir17(ctx.paths.memoriesDir)).filter(scopeAllowed) : [];
2935
2993
  const cmForBootstrap = await loadCodeMap(ctx.paths);
2936
2994
  let existingModules = [];
2937
2995
  try {
@@ -2942,7 +3000,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2942
3000
  const bootstrap = assessBootstrapState({
2943
3001
  projectContextRaw: pcRaw,
2944
3002
  memories: allForBootstrap,
2945
- codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files) : [],
3003
+ codeFiles: cmForBootstrap ? Object.keys(cmForBootstrap.files).filter((file) => existsSync22(path13.join(ctx.paths.root, file))) : [],
2946
3004
  existingModules
2947
3005
  });
2948
3006
  if (bootstrap.state !== "ready" && bootstrap.metrics.mainAreas > 0) {
@@ -3133,7 +3191,7 @@ function oneLine(value) {
3133
3191
  return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
3134
3192
  }
3135
3193
  function serverVersion() {
3136
- return true ? "0.42.1" : "dev";
3194
+ return true ? "0.43.2" : "dev";
3137
3195
  }
3138
3196
 
3139
3197
  // src/tools/code-map.ts
@@ -4306,6 +4364,7 @@ ${template}\`\`\`
4306
4364
  // src/prompts/bootstrap-repo.ts
4307
4365
  import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
4308
4366
  import { existsSync as existsSync29 } from "fs";
4367
+ import path14 from "path";
4309
4368
  import {
4310
4369
  assessBootstrapState as assessBootstrapState2,
4311
4370
  loadCodeMap as loadCodeMap4,
@@ -4333,7 +4392,7 @@ async function currentAssessment(ctx) {
4333
4392
  return assessBootstrapState2({
4334
4393
  projectContextRaw,
4335
4394
  memories,
4336
- codeFiles: codeMap ? Object.keys(codeMap.files) : [],
4395
+ codeFiles: codeMap ? Object.keys(codeMap.files).filter((file) => existsSync29(path14.join(ctx.paths.root, file))) : [],
4337
4396
  existingModules
4338
4397
  });
4339
4398
  }
@@ -4561,7 +4620,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
4561
4620
  // src/server.ts
4562
4621
  import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
4563
4622
  var SERVER_NAME = "hivelore";
4564
- var SERVER_VERSION = "0.42.1";
4623
+ var SERVER_VERSION = "0.43.2";
4565
4624
  function jsonResult(data) {
4566
4625
  return {
4567
4626
  content: [