@hivelore/cli 0.33.0 → 0.35.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/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  preCommitCheck,
11
11
  readPresumedCorrectTargets,
12
12
  runHaiveMcpStdio
13
- } from "./chunk-XDB7EKL4.js";
13
+ } from "./chunk-ZR7UPTRR.js";
14
14
  import {
15
15
  registerMemoryPending
16
16
  } from "./chunk-OYJKHD22.js";
@@ -208,7 +208,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
208
208
  if (!f) continue;
209
209
  counts.set(f, (counts.get(f) ?? 0) + 1);
210
210
  }
211
- let entries = [...counts.entries()].map(([path46, changes]) => ({ path: path46, changes }));
211
+ let entries = [...counts.entries()].map(([path47, changes]) => ({ path: path47, changes }));
212
212
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
213
213
  if (lowerPaths.length > 0) {
214
214
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -275,7 +275,7 @@ async function lintMemoriesAsync(root, options = {}) {
275
275
  const loaded = await loadMemoriesFromDir2(paths.memoriesDir);
276
276
  const usage = await loadUsageIndex(paths);
277
277
  const codeMap = await loadCodeMap(paths);
278
- const trackedFiles = gitTrackedFiles(root);
278
+ const trackedFiles2 = gitTrackedFiles(root);
279
279
  const ANCHOR_TYPES = /* @__PURE__ */ new Set(["decision", "architecture", "gotcha"]);
280
280
  const actionableWords = /\b(always|never|prefer|use|run|avoid|because|instead|why|rationale|do not|must|should|require|required|requires|fix|fail|failed|fails|prevent|prevents|allow|allows|lets|ensure|ensures|catch|catches)\b/i;
281
281
  for (const { filePath, memory: memory2 } of loaded) {
@@ -322,9 +322,9 @@ async function lintMemoriesAsync(root, options = {}) {
322
322
  message: "Reads like generic best practice a capable model already follows. Hivelore's value is UNGUESSABLE team knowledge \u2014 add the concrete, arbitrary specifics (exact names, values, formats, magic numbers) or consider removing it to keep briefings high-signal."
323
323
  });
324
324
  }
325
- const suggestedAnchors = suggestAnchors(root, { filePath, memory: memory2 }, codeMap, trackedFiles);
326
- const isUnanchoredSeed = fm.tags.includes("stack-pack") && fm.tags.includes("needs_anchor");
327
- if (ANCHOR_TYPES.has(fm.type) && fm.anchor.paths.length === 0 && fm.status === "validated" && !isUnanchoredSeed) {
325
+ const isStackSeed = fm.tags.includes("stack-pack");
326
+ const suggestedAnchors = isStackSeed ? { paths: [], symbols: [] } : suggestAnchors(root, { filePath, memory: memory2 }, codeMap, trackedFiles2);
327
+ if (ANCHOR_TYPES.has(fm.type) && fm.anchor.paths.length === 0 && fm.status === "validated" && !isStackSeed) {
328
328
  out.push({
329
329
  file: filePath,
330
330
  id: fm.id,
@@ -435,14 +435,14 @@ function titleFromId(id) {
435
435
  const withoutDate = id.replace(/^\d{4}-\d{2}-\d{2}-/, "");
436
436
  return withoutDate.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
437
437
  }
438
- function suggestAnchors(root, loaded, codeMap, trackedFiles) {
438
+ function suggestAnchors(root, loaded, codeMap, trackedFiles2) {
439
439
  const body = loaded.memory.body;
440
440
  const paths = /* @__PURE__ */ new Set();
441
441
  const symbols = /* @__PURE__ */ new Set();
442
442
  for (const match of body.matchAll(/`([^`\n]+\.[A-Za-z0-9]+)`|(?:^|\s)([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)/gm)) {
443
443
  const candidate = (match[1] ?? match[2] ?? "").replace(/^\.?\//, "");
444
444
  if (!candidate || candidate.startsWith("http")) continue;
445
- if (existsSync(path2.join(root, candidate)) && isSafeAnchorPath(candidate, trackedFiles)) {
445
+ if (existsSync(path2.join(root, candidate)) && isSafeAnchorPath(candidate, trackedFiles2)) {
446
446
  paths.add(candidate);
447
447
  }
448
448
  }
@@ -452,7 +452,7 @@ function suggestAnchors(root, loaded, codeMap, trackedFiles) {
452
452
  for (const exp of entry.exports) {
453
453
  if (!exp.name || exp.name.length < 4) continue;
454
454
  if (lowered.includes(exp.name.toLowerCase())) {
455
- if (isSafeAnchorPath(file, trackedFiles)) {
455
+ if (isSafeAnchorPath(file, trackedFiles2)) {
456
456
  paths.add(file);
457
457
  symbols.add(exp.name);
458
458
  }
@@ -477,12 +477,12 @@ function gitTrackedFiles(root) {
477
477
  const files = result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
478
478
  return new Set(files);
479
479
  }
480
- function isSafeAnchorPath(file, trackedFiles) {
480
+ function isSafeAnchorPath(file, trackedFiles2) {
481
481
  const normalized = file.replace(/\\/g, "/").replace(/^\.?\//, "");
482
482
  if (normalized.startsWith(".ai/.cache/") || normalized.startsWith(".ai/.runtime/")) return false;
483
483
  if (normalized.includes("/node_modules/") || normalized.startsWith("node_modules/")) return false;
484
484
  if (normalized.includes("/dist/") || normalized.startsWith("dist/")) return false;
485
- if (trackedFiles && !trackedFiles.has(normalized)) return false;
485
+ if (trackedFiles2 && !trackedFiles2.has(normalized)) return false;
486
486
  return true;
487
487
  }
488
488
  function nearDuplicatePairs(loaded) {
@@ -728,20 +728,7 @@ async function projectContextVersionStatus(root, paths) {
728
728
  async function refreshCodeMap(root, paths, force) {
729
729
  const existing = await loadCodeMap2(paths);
730
730
  if (existing && !force) return false;
731
- const map = await buildCodeMap(root, {
732
- includeUntracked: true,
733
- excludeDirs: [
734
- "node_modules",
735
- "dist",
736
- "build",
737
- "out",
738
- ".git",
739
- ".next",
740
- ".turbo",
741
- ".vitest-cache",
742
- "coverage"
743
- ]
744
- });
731
+ const map = await buildCodeMap(root, { includeUntracked: true });
745
732
  if (existing && existing.root === map.root && JSON.stringify(existing.files) === JSON.stringify(map.files)) {
746
733
  return false;
747
734
  }
@@ -1355,6 +1342,7 @@ import path6 from "path";
1355
1342
  import "commander";
1356
1343
  import {
1357
1344
  buildCodeMap as buildCodeMap2,
1345
+ CODE_MAP_DEFAULT_EXCLUDE,
1358
1346
  codeMapPath,
1359
1347
  findProjectRoot as findProjectRoot4,
1360
1348
  loadCodeMap as loadCodeMap4,
@@ -1383,18 +1371,7 @@ function registerIndexCode(program2) {
1383
1371
  ui.info(`Indexing source files in ${root}\u2026`);
1384
1372
  const map = await buildCodeMap2(root, {
1385
1373
  includeUntracked: true,
1386
- excludeDirs: [
1387
- "node_modules",
1388
- "dist",
1389
- "build",
1390
- "out",
1391
- ".git",
1392
- ".next",
1393
- ".turbo",
1394
- ".vitest-cache",
1395
- "coverage",
1396
- ...extraExcludes
1397
- ]
1374
+ excludeDirs: [...CODE_MAP_DEFAULT_EXCLUDE, ...extraExcludes]
1398
1375
  });
1399
1376
  await saveCodeMap2(paths, map);
1400
1377
  const fileCount = Object.keys(map.files).length;
@@ -1504,14 +1481,14 @@ function isCodeMapStale(root, generatedAt, files) {
1504
1481
  // src/commands/init.ts
1505
1482
  import { execFile as execFile2 } from "child_process";
1506
1483
  import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
1507
- import { existsSync as existsSync11 } from "fs";
1508
- import path12 from "path";
1484
+ import { existsSync as existsSync12 } from "fs";
1485
+ import path13 from "path";
1509
1486
  import { spawnSync as spawnSync3 } from "child_process";
1510
1487
  import { promisify as promisify2 } from "util";
1511
1488
  import "commander";
1512
1489
  import {
1513
1490
  AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
1514
- BRIDGE_TARGETS,
1491
+ BRIDGE_TARGETS as BRIDGE_TARGETS2,
1515
1492
  buildCodeMap as buildCodeMap3,
1516
1493
  buildFrontmatter as buildFrontmatter2,
1517
1494
  detectStacksFromManifests,
@@ -1523,20 +1500,93 @@ import {
1523
1500
  serializeMemory as serializeMemory3
1524
1501
  } from "@hivelore/core";
1525
1502
 
1503
+ // src/utils/doc-files.ts
1504
+ import { readdirSync } from "fs";
1505
+ function findDocFile(root, stem) {
1506
+ let entries;
1507
+ try {
1508
+ entries = readdirSync(root);
1509
+ } catch {
1510
+ return void 0;
1511
+ }
1512
+ const re = new RegExp(`^${stem}(\\.(md|markdown|txt|rst))?$`, "i");
1513
+ return entries.find((e) => re.test(e));
1514
+ }
1515
+
1516
+ // src/utils/bridge-detect.ts
1517
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
1518
+ import { homedir } from "os";
1519
+ import path7 from "path";
1520
+ import { BRIDGE_TARGET_PATH, BRIDGE_TARGETS } from "@hivelore/core";
1521
+ var VSCODE_EXTENSION_SIGNALS = {
1522
+ copilot: ["github.copilot"],
1523
+ cline: ["saoudrizwan.claude-dev"],
1524
+ roo: ["rooveterinaryinc.roo"],
1525
+ cody: ["sourcegraph.cody"],
1526
+ continue: ["continue.continue"]
1527
+ };
1528
+ var HOME_SIGNALS = {
1529
+ claude: [".claude", ".claude.json"],
1530
+ cursor: [".cursor"],
1531
+ windsurf: [".codeium/windsurf", ".windsurf"],
1532
+ gemini: [".gemini"],
1533
+ continue: [".continue"],
1534
+ aider: [".aider.conf.yml", ".aider"],
1535
+ zed: [".config/zed", "Library/Application Support/Zed"],
1536
+ copilot: [".config/github-copilot"]
1537
+ };
1538
+ var ENV_SIGNALS = {
1539
+ claude: ["CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"],
1540
+ cursor: ["CURSOR_AGENT"],
1541
+ gemini: ["GEMINI_CLI"],
1542
+ aider: ["AIDER_MODEL"]
1543
+ };
1544
+ function vscodeExtensionIds(home) {
1545
+ const ids = [];
1546
+ for (const dir of [".vscode/extensions", ".vscode-server/extensions", ".vscode-oss/extensions"]) {
1547
+ try {
1548
+ ids.push(...readdirSync2(path7.join(home, dir)).map((e) => e.toLowerCase()));
1549
+ } catch {
1550
+ }
1551
+ }
1552
+ return ids;
1553
+ }
1554
+ function detectBridgeTargets(root, env = process.env, home = homedir()) {
1555
+ const reasons = { agents: "universal" };
1556
+ const extensions = vscodeExtensionIds(home);
1557
+ for (const target of BRIDGE_TARGETS) {
1558
+ if (target === "agents") continue;
1559
+ if (existsSync6(path7.join(root, BRIDGE_TARGET_PATH[target]))) {
1560
+ reasons[target] = "repo file";
1561
+ continue;
1562
+ }
1563
+ const homeHit = (HOME_SIGNALS[target] ?? []).some((p) => existsSync6(path7.join(home, p)));
1564
+ const envHit = (ENV_SIGNALS[target] ?? []).some((v) => env[v]);
1565
+ const extHit = (VSCODE_EXTENSION_SIGNALS[target] ?? []).some(
1566
+ (prefix) => extensions.some((id) => id.startsWith(prefix))
1567
+ );
1568
+ if (homeHit || envHit || extHit) reasons[target] = "installed";
1569
+ }
1570
+ return {
1571
+ targets: BRIDGE_TARGETS.filter((t) => t in reasons),
1572
+ reasons
1573
+ };
1574
+ }
1575
+
1526
1576
  // src/utils/bridge-files.ts
1527
1577
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
1528
- import { existsSync as existsSync6 } from "fs";
1529
- import path7 from "path";
1578
+ import { existsSync as existsSync7 } from "fs";
1579
+ import path8 from "path";
1530
1580
  import {
1531
1581
  BRIDGE_MARKERS,
1532
- BRIDGE_TARGET_PATH,
1582
+ BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
1533
1583
  generateBridges,
1534
1584
  isRetiredMemory,
1535
1585
  loadMemoriesFromDir as loadMemoriesFromDir5
1536
1586
  } from "@hivelore/core";
1537
1587
  async function writeBridgeFiles(root, paths, opts) {
1538
1588
  const result = { created: [], updated: [], unchanged: [], skipped: [], warnings: [] };
1539
- if (!existsSync6(paths.memoriesDir)) return result;
1589
+ if (!existsSync7(paths.memoriesDir)) return result;
1540
1590
  const allLoaded = await loadMemoriesFromDir5(paths.memoriesDir);
1541
1591
  const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory(m.frontmatter, m.body));
1542
1592
  const sensors = [];
@@ -1554,8 +1604,8 @@ async function writeBridgeFiles(root, paths, opts) {
1554
1604
  const maxMemories = Math.max(1, opts.maxMemories ?? 8);
1555
1605
  const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
1556
1606
  for (const output of outputs) {
1557
- const targetFile = path7.join(root, output.path);
1558
- const fileExists = existsSync6(targetFile);
1607
+ const targetFile = path8.join(root, output.path);
1608
+ const fileExists = existsSync7(targetFile);
1559
1609
  if (opts.onlyExisting && !fileExists) continue;
1560
1610
  if (opts.dryRun) {
1561
1611
  if (!fileExists) {
@@ -1574,7 +1624,7 @@ async function writeBridgeFiles(root, paths, opts) {
1574
1624
  }
1575
1625
  continue;
1576
1626
  }
1577
- await mkdir2(path7.dirname(targetFile), { recursive: true });
1627
+ await mkdir2(path8.dirname(targetFile), { recursive: true });
1578
1628
  if (!fileExists) {
1579
1629
  await writeFile3(targetFile, output.content, "utf8");
1580
1630
  result.created.push(output.path);
@@ -1597,10 +1647,10 @@ async function writeBridgeFiles(root, paths, opts) {
1597
1647
  return result;
1598
1648
  }
1599
1649
  async function getBridgeFileStatuses(root, paths, opts) {
1600
- if (!existsSync6(paths.memoriesDir)) {
1650
+ if (!existsSync7(paths.memoriesDir)) {
1601
1651
  return opts.targets.map((target) => ({
1602
1652
  target,
1603
- path: BRIDGE_TARGET_PATH[target],
1653
+ path: BRIDGE_TARGET_PATH2[target],
1604
1654
  exists: false,
1605
1655
  state: "missing",
1606
1656
  wouldChange: false,
@@ -1627,8 +1677,8 @@ async function getBridgeFileStatuses(root, paths, opts) {
1627
1677
  });
1628
1678
  const statuses = [];
1629
1679
  for (const output of outputs) {
1630
- const targetFile = path7.join(root, output.path);
1631
- const exists = existsSync6(targetFile);
1680
+ const targetFile = path8.join(root, output.path);
1681
+ const exists = existsSync7(targetFile);
1632
1682
  if (!exists) {
1633
1683
  statuses.push({
1634
1684
  target: output.target,
@@ -1784,18 +1834,18 @@ function legacyManagedRange(text) {
1784
1834
 
1785
1835
  // src/commands/agent.ts
1786
1836
  import { spawnSync as spawnSync2 } from "child_process";
1787
- import { existsSync as existsSync8 } from "fs";
1837
+ import { existsSync as existsSync9 } from "fs";
1788
1838
  import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
1789
1839
  import os2 from "os";
1790
- import path9 from "path";
1840
+ import path10 from "path";
1791
1841
  import { createInterface } from "readline/promises";
1792
1842
  import "commander";
1793
1843
  import { findProjectRoot as findProjectRoot5, resolveHaivePaths as resolveHaivePaths5 } from "@hivelore/core";
1794
1844
 
1795
1845
  // src/commands/init-mcp-setup.ts
1796
1846
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
1797
- import { existsSync as existsSync7 } from "fs";
1798
- import path8 from "path";
1847
+ import { existsSync as existsSync8 } from "fs";
1848
+ import path9 from "path";
1799
1849
  import os from "os";
1800
1850
  var HOME = os.homedir();
1801
1851
  var HAIVE_MCP_ENTRY = {
@@ -1810,14 +1860,14 @@ function projectMcpEntry(root) {
1810
1860
  };
1811
1861
  }
1812
1862
  function cursorMcpPath() {
1813
- return path8.join(HOME, ".cursor", "mcp.json");
1863
+ return path9.join(HOME, ".cursor", "mcp.json");
1814
1864
  }
1815
1865
  async function configureCursor() {
1816
1866
  const mcpPath = cursorMcpPath();
1817
- const cursorDir = path8.join(HOME, ".cursor");
1818
- if (!existsSync7(cursorDir)) return { client: "Cursor", status: "not_installed" };
1867
+ const cursorDir = path9.join(HOME, ".cursor");
1868
+ if (!existsSync8(cursorDir)) return { client: "Cursor", status: "not_installed" };
1819
1869
  let config = {};
1820
- if (existsSync7(mcpPath)) {
1870
+ if (existsSync8(mcpPath)) {
1821
1871
  try {
1822
1872
  config = JSON.parse(await readFile4(mcpPath, "utf8"));
1823
1873
  } catch {
@@ -1832,16 +1882,16 @@ async function configureCursor() {
1832
1882
  }
1833
1883
  function vscodeMcpPath() {
1834
1884
  const candidates = [
1835
- path8.join(HOME, ".config", "Code", "User", "mcp.json"),
1885
+ path9.join(HOME, ".config", "Code", "User", "mcp.json"),
1836
1886
  // Linux
1837
- path8.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1887
+ path9.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1838
1888
  // macOS
1839
- path8.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1889
+ path9.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1840
1890
  // Windows
1841
- path8.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1891
+ path9.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1842
1892
  ];
1843
1893
  for (const c of candidates) {
1844
- if (existsSync7(path8.dirname(c))) return c;
1894
+ if (existsSync8(path9.dirname(c))) return c;
1845
1895
  }
1846
1896
  return null;
1847
1897
  }
@@ -1849,7 +1899,7 @@ async function configureVSCode() {
1849
1899
  const mcpPath = vscodeMcpPath();
1850
1900
  if (!mcpPath) return { client: "VS Code", status: "not_installed" };
1851
1901
  let config = {};
1852
- if (existsSync7(mcpPath)) {
1902
+ if (existsSync8(mcpPath)) {
1853
1903
  try {
1854
1904
  config = JSON.parse(await readFile4(mcpPath, "utf8"));
1855
1905
  } catch {
@@ -1858,24 +1908,24 @@ async function configureVSCode() {
1858
1908
  config.servers ??= {};
1859
1909
  if (config.servers["hivelore"] || config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
1860
1910
  config.servers["hivelore"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1861
- await mkdir3(path8.dirname(mcpPath), { recursive: true });
1911
+ await mkdir3(path9.dirname(mcpPath), { recursive: true });
1862
1912
  await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1863
1913
  return { client: "VS Code", status: "configured", path: mcpPath };
1864
1914
  }
1865
1915
  function claudeConfigPath() {
1866
- const p = path8.join(HOME, ".claude.json");
1867
- if (existsSync7(p)) return p;
1868
- const p2 = path8.join(HOME, ".config", "claude", "claude.json");
1869
- if (existsSync7(path8.dirname(p2))) return p2;
1916
+ const p = path9.join(HOME, ".claude.json");
1917
+ if (existsSync8(p)) return p;
1918
+ const p2 = path9.join(HOME, ".config", "claude", "claude.json");
1919
+ if (existsSync8(path9.dirname(p2))) return p2;
1870
1920
  return null;
1871
1921
  }
1872
1922
  async function configureClaude() {
1873
- const cfgPath = claudeConfigPath() ?? path8.join(HOME, ".claude.json");
1874
- if (!existsSync7(cfgPath) && !existsSync7(path8.join(HOME, ".claude"))) {
1923
+ const cfgPath = claudeConfigPath() ?? path9.join(HOME, ".claude.json");
1924
+ if (!existsSync8(cfgPath) && !existsSync8(path9.join(HOME, ".claude"))) {
1875
1925
  return { client: "Claude Code", status: "not_installed" };
1876
1926
  }
1877
1927
  let config = {};
1878
- if (existsSync7(cfgPath)) {
1928
+ if (existsSync8(cfgPath)) {
1879
1929
  try {
1880
1930
  config = JSON.parse(await readFile4(cfgPath, "utf8"));
1881
1931
  } catch {
@@ -1889,11 +1939,11 @@ async function configureClaude() {
1889
1939
  }
1890
1940
  function windsurfMcpPath() {
1891
1941
  const candidates = [
1892
- path8.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1893
- path8.join(HOME, ".windsurf", "mcp.json")
1942
+ path9.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1943
+ path9.join(HOME, ".windsurf", "mcp.json")
1894
1944
  ];
1895
1945
  for (const c of candidates) {
1896
- if (existsSync7(path8.dirname(c))) return c;
1946
+ if (existsSync8(path9.dirname(c))) return c;
1897
1947
  }
1898
1948
  return null;
1899
1949
  }
@@ -1901,7 +1951,7 @@ async function configureWindsurf() {
1901
1951
  const mcpPath = windsurfMcpPath();
1902
1952
  if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
1903
1953
  let config = {};
1904
- if (existsSync7(mcpPath)) {
1954
+ if (existsSync8(mcpPath)) {
1905
1955
  try {
1906
1956
  config = JSON.parse(await readFile4(mcpPath, "utf8"));
1907
1957
  } catch {
@@ -1910,7 +1960,7 @@ async function configureWindsurf() {
1910
1960
  config.mcpServers ??= {};
1911
1961
  if (config.mcpServers["hivelore"] || config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
1912
1962
  config.mcpServers["hivelore"] = HAIVE_MCP_ENTRY;
1913
- await mkdir3(path8.dirname(mcpPath), { recursive: true });
1963
+ await mkdir3(path9.dirname(mcpPath), { recursive: true });
1914
1964
  await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1915
1965
  return { client: "Windsurf", status: "configured", path: mcpPath };
1916
1966
  }
@@ -1931,9 +1981,9 @@ async function configureProjectMcpClients(root) {
1931
1981
  const entry = projectMcpEntry(root);
1932
1982
  const results = [];
1933
1983
  try {
1934
- const cursorPath = path8.join(root, ".cursor", "mcp.json");
1984
+ const cursorPath = path9.join(root, ".cursor", "mcp.json");
1935
1985
  let config = {};
1936
- if (existsSync7(cursorPath)) {
1986
+ if (existsSync8(cursorPath)) {
1937
1987
  try {
1938
1988
  config = JSON.parse(await readFile4(cursorPath, "utf8"));
1939
1989
  } catch {
@@ -1942,16 +1992,16 @@ async function configureProjectMcpClients(root) {
1942
1992
  config.mcpServers ??= {};
1943
1993
  delete config.mcpServers["haive"];
1944
1994
  config.mcpServers["hivelore"] = entry;
1945
- await mkdir3(path8.dirname(cursorPath), { recursive: true });
1995
+ await mkdir3(path9.dirname(cursorPath), { recursive: true });
1946
1996
  await writeFile4(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1947
1997
  results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
1948
1998
  } catch (err) {
1949
1999
  results.push({ client: "Cursor (project)", status: "error", error: String(err) });
1950
2000
  }
1951
2001
  try {
1952
- const vscodePath = path8.join(root, ".vscode", "mcp.json");
2002
+ const vscodePath = path9.join(root, ".vscode", "mcp.json");
1953
2003
  let config = {};
1954
- if (existsSync7(vscodePath)) {
2004
+ if (existsSync8(vscodePath)) {
1955
2005
  try {
1956
2006
  config = JSON.parse(await readFile4(vscodePath, "utf8"));
1957
2007
  } catch {
@@ -1960,16 +2010,16 @@ async function configureProjectMcpClients(root) {
1960
2010
  config.servers ??= {};
1961
2011
  delete config.servers["haive"];
1962
2012
  config.servers["hivelore"] = { ...entry, type: "stdio" };
1963
- await mkdir3(path8.dirname(vscodePath), { recursive: true });
2013
+ await mkdir3(path9.dirname(vscodePath), { recursive: true });
1964
2014
  await writeFile4(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
1965
2015
  results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
1966
2016
  } catch (err) {
1967
2017
  results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
1968
2018
  }
1969
2019
  try {
1970
- const mcpPath = path8.join(root, ".mcp.json");
2020
+ const mcpPath = path9.join(root, ".mcp.json");
1971
2021
  let config = {};
1972
- if (existsSync7(mcpPath)) {
2022
+ if (existsSync8(mcpPath)) {
1973
2023
  try {
1974
2024
  config = JSON.parse(await readFile4(mcpPath, "utf8"));
1975
2025
  } catch {
@@ -2044,9 +2094,9 @@ async function detectAgentMode(dir) {
2044
2094
  const root = findProjectRoot5(dir);
2045
2095
  const paths = resolveHaivePaths5(root);
2046
2096
  const projectMcp = [
2047
- { client: "Claude Code", path: path9.join(root, ".mcp.json"), present: existsSync8(path9.join(root, ".mcp.json")) },
2048
- { client: "Cursor", path: path9.join(root, ".cursor", "mcp.json"), present: existsSync8(path9.join(root, ".cursor", "mcp.json")) },
2049
- { client: "VS Code", path: path9.join(root, ".vscode", "mcp.json"), present: existsSync8(path9.join(root, ".vscode", "mcp.json")) }
2097
+ { client: "Claude Code", path: path10.join(root, ".mcp.json"), present: existsSync9(path10.join(root, ".mcp.json")) },
2098
+ { client: "Cursor", path: path10.join(root, ".cursor", "mcp.json"), present: existsSync9(path10.join(root, ".cursor", "mcp.json")) },
2099
+ { client: "VS Code", path: path10.join(root, ".vscode", "mcp.json"), present: existsSync9(path10.join(root, ".vscode", "mcp.json")) }
2050
2100
  ];
2051
2101
  const installedAgents = [
2052
2102
  { agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
@@ -2061,7 +2111,7 @@ async function detectAgentMode(dir) {
2061
2111
  const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `hivelore run -- ${wrapperAgent.command}` : 'hivelore briefing --task "..." --files "..."';
2062
2112
  return {
2063
2113
  root,
2064
- initialized: existsSync8(paths.haiveDir),
2114
+ initialized: existsSync9(paths.haiveDir),
2065
2115
  project_mcp: projectMcp,
2066
2116
  installed_agents: installedAgents,
2067
2117
  recommended_mode: recommendedMode,
@@ -2069,9 +2119,9 @@ async function detectAgentMode(dir) {
2069
2119
  };
2070
2120
  }
2071
2121
  async function writeAgentModeRecord(paths, detection, skippedReason) {
2072
- const dir = path9.join(paths.runtimeDir, "enforcement");
2122
+ const dir = path10.join(paths.runtimeDir, "enforcement");
2073
2123
  await mkdir4(dir, { recursive: true });
2074
- const file = path9.join(dir, "agent-mode.json");
2124
+ const file = path10.join(dir, "agent-mode.json");
2075
2125
  const record = {
2076
2126
  selected_mode: detection.recommended_mode,
2077
2127
  recommended_command: detection.recommended_command,
@@ -2112,7 +2162,7 @@ async function configureCodexIfAvailable(root) {
2112
2162
  "mcp",
2113
2163
  "--stdio"
2114
2164
  ], { encoding: "utf8" });
2115
- if (result.status === 0) return { client: "Codex", status: "configured", path: path9.join(os2.homedir(), ".codex", "config.toml") };
2165
+ if (result.status === 0) return { client: "Codex", status: "configured", path: path10.join(os2.homedir(), ".codex", "config.toml") };
2116
2166
  return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
2117
2167
  }
2118
2168
  function commandExists(command) {
@@ -2139,7 +2189,7 @@ function printDetection(detection, json) {
2139
2189
  console.log(ui.dim(` root: ${detection.root}`));
2140
2190
  console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
2141
2191
  for (const cfg of detection.project_mcp) {
2142
- console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path9.relative(detection.root, cfg.path))}`);
2192
+ console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path10.relative(detection.root, cfg.path))}`);
2143
2193
  }
2144
2194
  for (const agent of detection.installed_agents) {
2145
2195
  const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
@@ -2168,8 +2218,8 @@ function printSetupResult(result) {
2168
2218
 
2169
2219
  // src/commands/init-bootstrap.ts
2170
2220
  import { readdir, readFile as readFile5 } from "fs/promises";
2171
- import { existsSync as existsSync9, readdirSync } from "fs";
2172
- import path10 from "path";
2221
+ import { existsSync as existsSync10, readdirSync as readdirSync3 } from "fs";
2222
+ import path11 from "path";
2173
2223
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
2174
2224
  "node_modules",
2175
2225
  "dist",
@@ -2255,12 +2305,12 @@ function detectKeyDeps(allDeps) {
2255
2305
  return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
2256
2306
  }
2257
2307
  function detectLanguage(root) {
2258
- if (existsSync9(path10.join(root, "tsconfig.json"))) return "TypeScript";
2259
- if (existsSync9(path10.join(root, "pyproject.toml")) || existsSync9(path10.join(root, "setup.py"))) return "Python";
2260
- if (existsSync9(path10.join(root, "go.mod"))) return "Go";
2261
- if (existsSync9(path10.join(root, "pom.xml")) || existsSync9(path10.join(root, "build.gradle"))) return "Java/Kotlin";
2262
- if (existsSync9(path10.join(root, "Cargo.toml"))) return "Rust";
2263
- if (existsSync9(path10.join(root, "package.json"))) {
2308
+ if (existsSync10(path11.join(root, "tsconfig.json"))) return "TypeScript";
2309
+ if (existsSync10(path11.join(root, "pyproject.toml")) || existsSync10(path11.join(root, "setup.py"))) return "Python";
2310
+ if (existsSync10(path11.join(root, "go.mod"))) return "Go";
2311
+ if (existsSync10(path11.join(root, "pom.xml")) || existsSync10(path11.join(root, "build.gradle"))) return "Java/Kotlin";
2312
+ if (existsSync10(path11.join(root, "Cargo.toml"))) return "Rust";
2313
+ if (existsSync10(path11.join(root, "package.json"))) {
2264
2314
  return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
2265
2315
  }
2266
2316
  return "Unknown";
@@ -2270,7 +2320,7 @@ function hasSourceWithExt(root, exts) {
2270
2320
  const scanDir = (dir, depth) => {
2271
2321
  let entries;
2272
2322
  try {
2273
- entries = readdirSync(dir, { withFileTypes: true });
2323
+ entries = readdirSync3(dir, { withFileTypes: true });
2274
2324
  } catch {
2275
2325
  return false;
2276
2326
  }
@@ -2280,7 +2330,7 @@ function hasSourceWithExt(root, exts) {
2280
2330
  if (depth <= 0) return false;
2281
2331
  for (const entry of entries) {
2282
2332
  if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
2283
- if (scanDir(path10.join(dir, entry.name), depth - 1)) return true;
2333
+ if (scanDir(path11.join(dir, entry.name), depth - 1)) return true;
2284
2334
  }
2285
2335
  }
2286
2336
  return false;
@@ -2301,7 +2351,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
2301
2351
  if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
2302
2352
  if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
2303
2353
  if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
2304
- if (existsSync9("pom.xml")) return "Java backend";
2354
+ if (existsSync10("pom.xml")) return "Java backend";
2305
2355
  return "Application";
2306
2356
  }
2307
2357
  async function scanDirs(root, maxDepth = 2) {
@@ -2317,9 +2367,9 @@ async function scanDirs(root, maxDepth = 2) {
2317
2367
  for (const entry of entries) {
2318
2368
  if (!entry.isDirectory()) continue;
2319
2369
  if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
2320
- const rel = path10.relative(root, path10.join(dir, entry.name));
2370
+ const rel = path11.relative(root, path11.join(dir, entry.name));
2321
2371
  results.push(rel);
2322
- await walk(path10.join(dir, entry.name), depth + 1);
2372
+ await walk(path11.join(dir, entry.name), depth + 1);
2323
2373
  }
2324
2374
  }
2325
2375
  await walk(root, 0);
@@ -2436,8 +2486,8 @@ function readmeExcerpt(readme) {
2436
2486
  }
2437
2487
  async function generateBootstrapContext(root) {
2438
2488
  let pkg = {};
2439
- const pkgPath = path10.join(root, "package.json");
2440
- if (existsSync9(pkgPath)) {
2489
+ const pkgPath = path11.join(root, "package.json");
2490
+ if (existsSync10(pkgPath)) {
2441
2491
  try {
2442
2492
  pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
2443
2493
  } catch {
@@ -2449,18 +2499,15 @@ async function generateBootstrapContext(root) {
2449
2499
  const language = detectLanguage(root);
2450
2500
  const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
2451
2501
  const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
2452
- const projectName = pkg.name ?? path10.basename(root);
2502
+ const projectName = pkg.name ?? path11.basename(root);
2453
2503
  const projectDesc = pkg.description ?? "";
2454
2504
  let readmeSummary = "";
2455
- for (const name of ["README.md", "readme.md", "README"]) {
2456
- const p = path10.join(root, name);
2457
- if (existsSync9(p)) {
2458
- try {
2459
- const content = await readFile5(p, "utf8");
2460
- readmeSummary = readmeExcerpt(content);
2461
- break;
2462
- } catch {
2463
- }
2505
+ const readmeName = findDocFile(root, "readme");
2506
+ if (readmeName) {
2507
+ try {
2508
+ const content = await readFile5(path11.join(root, readmeName), "utf8");
2509
+ readmeSummary = readmeExcerpt(content);
2510
+ } catch {
2464
2511
  }
2465
2512
  }
2466
2513
  const dirs = await scanDirs(root, 2);
@@ -2513,7 +2560,7 @@ async function generateBootstrapContext(root) {
2513
2560
  "",
2514
2561
  `## Gotchas`,
2515
2562
  `TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.`,
2516
- `(Run \`hivelore memory import-changelog\` or \`hivelore memory import README.md\` to seed these automatically.)`,
2563
+ `(Seed these with \`hivelore memory import --from ${findDocFile(root, "changelog") ?? "CHANGELOG.md"} --changelog\` or \`hivelore memory import --from ${findDocFile(root, "readme") ?? "README.md"}\` via your AI client.)`,
2517
2564
  ""
2518
2565
  ];
2519
2566
  return lines.join("\n");
@@ -2521,8 +2568,8 @@ async function generateBootstrapContext(root) {
2521
2568
 
2522
2569
  // src/commands/init-stack-packs.ts
2523
2570
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2524
- import { existsSync as existsSync10 } from "fs";
2525
- import path11 from "path";
2571
+ import { existsSync as existsSync11 } from "fs";
2572
+ import path12 from "path";
2526
2573
  import {
2527
2574
  buildFrontmatter,
2528
2575
  loadMemoriesFromDir as loadMemoriesFromDir6,
@@ -3626,7 +3673,7 @@ async function seedStackPack(haivePaths, stack) {
3626
3673
  const existingTopics = /* @__PURE__ */ new Set();
3627
3674
  const existingSignatures = /* @__PURE__ */ new Set();
3628
3675
  const handWrittenSlugs = [];
3629
- if (existsSync10(haivePaths.memoriesDir)) {
3676
+ if (existsSync11(haivePaths.memoriesDir)) {
3630
3677
  for (const { memory: memory2 } of await loadMemoriesFromDir6(haivePaths.memoriesDir)) {
3631
3678
  if (memory2.frontmatter.topic) existingTopics.add(memory2.frontmatter.topic);
3632
3679
  existingSignatures.add(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
@@ -3671,7 +3718,7 @@ async function seedStackPack(haivePaths, stack) {
3671
3718
  ...sensor ? { sensor } : {}
3672
3719
  });
3673
3720
  const filePath = memoryFilePath(haivePaths, "team", fm.id);
3674
- if (existsSync10(filePath)) continue;
3721
+ if (existsSync11(filePath)) continue;
3675
3722
  const ruleSlug = combinedSlug.startsWith(`${stack}-`) ? combinedSlug.slice(stack.length + 1) : combinedSlug;
3676
3723
  const titleCase = (s) => s.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3677
3724
  const heading = `${titleCase(stack)}: ${titleCase(ruleSlug)}`;
@@ -3681,7 +3728,7 @@ ${mem.body}`;
3681
3728
  const content = serializeMemory2({ frontmatter: fm, body: `${titledBody}
3682
3729
 
3683
3730
  ${SEED_FOOTER(stack)}` });
3684
- await mkdir5(path11.dirname(filePath), { recursive: true });
3731
+ await mkdir5(path12.dirname(filePath), { recursive: true });
3685
3732
  await writeFile6(filePath, content, "utf8");
3686
3733
  existingTopics.add(topic);
3687
3734
  existingSignatures.add(signature);
@@ -3708,7 +3755,7 @@ ${SEED_FOOTER(stack)}` });
3708
3755
 
3709
3756
  // src/commands/init.ts
3710
3757
  var execFileAsync = promisify2(execFile2);
3711
- var HAIVE_GITHUB_ACTION_REF = `v${"0.33.0"}`;
3758
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.35.0"}`;
3712
3759
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3713
3760
 
3714
3761
  > Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
@@ -3901,9 +3948,9 @@ function registerInit(program2) {
3901
3948
  "Initialize a Hivelore project \u2014 autopilot mode ON by default (zero human intervention).\n Auto-bootstraps project-context.md from local files and seeds detected stack packs.\n Seeds draft memories from git revert/hotfix history (--seed, on by default).\n Add --manual to control memory approval and session recaps yourself.\n Add --no-bootstrap and --stack none to disable the auto-features."
3902
3949
  ).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate any native agent bridge files").option(
3903
3950
  "--bridge-targets <list>",
3904
- `which agent bridges to generate: 'all' (default) | comma-list.
3905
- Available: ${BRIDGE_TARGETS.join(", ")}. Each carries top memories + block sensors.`,
3906
- "all"
3951
+ `which agent bridges to generate: 'auto' (default \u2014 clients detected on this machine/repo, plus AGENTS.md) | 'all' | comma-list.
3952
+ Available: ${BRIDGE_TARGETS2.join(", ")}. Each carries top memories + block sensors.`,
3953
+ "auto"
3907
3954
  ).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
3908
3955
  "--manual",
3909
3956
  "opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
@@ -3936,7 +3983,7 @@ function registerInit(program2) {
3936
3983
  "approve user-level AI client configuration prompts during agent setup",
3937
3984
  false
3938
3985
  ).option("--json", "emit a machine-readable summary on stdout (human logs go to stderr)", false).action(async (opts) => {
3939
- const root = path12.resolve(opts.dir);
3986
+ const root = path13.resolve(opts.dir);
3940
3987
  const paths = resolveHaivePaths6(root);
3941
3988
  const autopilot = opts.manual !== true;
3942
3989
  const json = opts.json === true;
@@ -3953,9 +4000,10 @@ function registerInit(program2) {
3953
4000
  gitCommitsScanned: 0,
3954
4001
  gitRevertsFound: 0,
3955
4002
  gitRecurring: 0,
3956
- bridgesWritten: 0
4003
+ bridgesWritten: 0,
4004
+ bridgeTargets: []
3957
4005
  };
3958
- if (existsSync11(paths.haiveDir)) {
4006
+ if (existsSync12(paths.haiveDir)) {
3959
4007
  ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
3960
4008
  }
3961
4009
  await mkdir6(paths.personalDir, { recursive: true });
@@ -3963,8 +4011,8 @@ function registerInit(program2) {
3963
4011
  await mkdir6(paths.moduleDir, { recursive: true });
3964
4012
  await mkdir6(paths.modulesContextDir, { recursive: true });
3965
4013
  await ensureAiRuntimeLayout(paths.runtimeDir);
3966
- await ensureAiCacheLayout(path12.join(paths.haiveDir, ".cache"));
3967
- if (!existsSync11(paths.projectContext)) {
4014
+ await ensureAiCacheLayout(path13.join(paths.haiveDir, ".cache"));
4015
+ if (!existsSync12(paths.projectContext)) {
3968
4016
  if (wantBootstrap) {
3969
4017
  ui.info("Bootstrapping project context from local files\u2026");
3970
4018
  try {
@@ -3977,11 +4025,11 @@ function registerInit(program2) {
3977
4025
  }
3978
4026
  } else {
3979
4027
  await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3980
- ui.success(`Created ${path12.relative(root, paths.projectContext)}`);
4028
+ ui.success(`Created ${path13.relative(root, paths.projectContext)}`);
3981
4029
  }
3982
4030
  }
3983
- const configExists = existsSync11(
3984
- path12.join(paths.haiveDir, "haive.config.json")
4031
+ const configExists = existsSync12(
4032
+ path13.join(paths.haiveDir, "haive.config.json")
3985
4033
  );
3986
4034
  if (!configExists) {
3987
4035
  await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
@@ -4031,11 +4079,14 @@ function registerInit(program2) {
4031
4079
  }
4032
4080
  }
4033
4081
  if (opts.bridges) {
4034
- const targets = resolveBridgeTargets(opts.bridgeTargets);
4082
+ const targets = resolveBridgeTargets(opts.bridgeTargets, root);
4035
4083
  const res = await writeBridgeFiles(root, paths, { targets });
4036
- await writeCursorHaiveRule(root);
4084
+ if (targets.includes("cursor")) {
4085
+ await writeCursorHaiveRule(root);
4086
+ }
4037
4087
  const made = res.created.length + res.updated.length;
4038
4088
  report.bridgesWritten = made;
4089
+ report.bridgeTargets = targets;
4039
4090
  if (res.created.length > 0) {
4040
4091
  ui.success(`Generated ${res.created.length} agent bridge(s): ${res.created.join(", ")}`);
4041
4092
  }
@@ -4048,13 +4099,13 @@ function registerInit(program2) {
4048
4099
  }
4049
4100
  const wantCi = opts.withCi || autopilot;
4050
4101
  if (wantCi) {
4051
- const ciPath = path12.join(root, ".github", "workflows", "haive-sync.yml");
4052
- if (existsSync11(ciPath)) {
4102
+ const ciPath = path13.join(root, ".github", "workflows", "haive-sync.yml");
4103
+ if (existsSync12(ciPath)) {
4053
4104
  ui.info("CI workflow already exists \u2014 skipped");
4054
4105
  } else {
4055
- await mkdir6(path12.dirname(ciPath), { recursive: true });
4106
+ await mkdir6(path13.dirname(ciPath), { recursive: true });
4056
4107
  await writeFile7(ciPath, CI_WORKFLOW, "utf8");
4057
- ui.success(`Created ${path12.relative(root, ciPath)}`);
4108
+ ui.success(`Created ${path13.relative(root, ciPath)}`);
4058
4109
  }
4059
4110
  }
4060
4111
  if (autopilot) {
@@ -4094,7 +4145,7 @@ function registerInit(program2) {
4094
4145
  interactive: process.stdin.isTTY
4095
4146
  });
4096
4147
  for (const r of agentSetup.project_results) {
4097
- if (r.status === "configured" && r.path) ui.success(`hivelore MCP project config written (${path12.relative(root, r.path)})`);
4148
+ if (r.status === "configured" && r.path) ui.success(`hivelore MCP project config written (${path13.relative(root, r.path)})`);
4098
4149
  else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
4099
4150
  }
4100
4151
  for (const r of agentSetup.global_results) {
@@ -4160,10 +4211,18 @@ function registerInit(program2) {
4160
4211
  console.log(ui.dim(" Review .ai/project-context.md and fill in the TODO sections."));
4161
4212
  console.log(ui.dim(" Or invoke the MCP prompt `bootstrap_project` for a richer AI-generated version."));
4162
4213
  }
4163
- console.log();
4164
- console.log(ui.dim(" Seed more memories instantly:"));
4165
- console.log(ui.dim(" hivelore memory import-changelog \u2014 from CHANGELOG.md"));
4166
- console.log(ui.dim(" hivelore memory import README.md \u2014 from README / docs"));
4214
+ const changelogFile = findDocFile(root, "changelog");
4215
+ const readmeFile = findDocFile(root, "readme");
4216
+ if (changelogFile || readmeFile) {
4217
+ console.log();
4218
+ console.log(ui.dim(" Seed more memories:"));
4219
+ if (changelogFile) {
4220
+ console.log(ui.dim(` hivelore memory import --from ${changelogFile} --changelog \u2014 extract breaking-change gotchas (no AI needed)`));
4221
+ }
4222
+ if (readmeFile) {
4223
+ console.log(ui.dim(` hivelore memory import --from ${readmeFile} \u2014 prepare an import prompt for your AI client`));
4224
+ }
4225
+ }
4167
4226
  } else {
4168
4227
  console.log(ui.bold("Next steps:"));
4169
4228
  if (!wantBootstrap) {
@@ -4189,7 +4248,42 @@ async function resolveStacksToSeed(root, stackOpt) {
4189
4248
  return stackOpt.split(",").map((s) => s.trim().toLowerCase()).filter(isValidStack);
4190
4249
  }
4191
4250
  async function collectNestedPackageDeps(root) {
4192
- const SKIP = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".git", ".next", "coverage", "vendor"]);
4251
+ const SKIP = /* @__PURE__ */ new Set([
4252
+ "node_modules",
4253
+ "dist",
4254
+ "build",
4255
+ "out",
4256
+ ".git",
4257
+ ".next",
4258
+ "coverage",
4259
+ "vendor",
4260
+ // Fixture/demo trees describe what the project TESTS AGAINST, not what it is built with.
4261
+ // Vite's playground/ pulls react+vue+tailwind+express into detection and seeds four
4262
+ // irrelevant stack packs on a build-tool repo.
4263
+ "playground",
4264
+ "playgrounds",
4265
+ "example",
4266
+ "examples",
4267
+ "fixtures",
4268
+ "__fixtures__",
4269
+ "e2e",
4270
+ "test",
4271
+ "tests",
4272
+ "demo",
4273
+ "demos",
4274
+ "template",
4275
+ "templates",
4276
+ "bench",
4277
+ "benchmarks",
4278
+ "samples",
4279
+ "sandbox",
4280
+ // Doc sites are built with their own framework (VitePress→vue, Docusaurus→react) — that is
4281
+ // the DOCS' stack, not the product's.
4282
+ "docs",
4283
+ "doc",
4284
+ "website"
4285
+ ]);
4286
+ const SKIP_PREFIX = /^(template|example|fixture|demo|sample)s?-/;
4193
4287
  const merged = {};
4194
4288
  let found = false;
4195
4289
  async function scan(dir, depth) {
@@ -4201,10 +4295,10 @@ async function collectNestedPackageDeps(root) {
4201
4295
  return;
4202
4296
  }
4203
4297
  for (const entry of entries) {
4204
- if (!entry.isDirectory() || entry.name.startsWith(".") || SKIP.has(entry.name)) continue;
4205
- const sub = path12.join(dir, entry.name);
4206
- const pkgPath = path12.join(sub, "package.json");
4207
- if (existsSync11(pkgPath)) {
4298
+ if (!entry.isDirectory() || entry.name.startsWith(".") || SKIP.has(entry.name) || SKIP_PREFIX.test(entry.name)) continue;
4299
+ const sub = path13.join(dir, entry.name);
4300
+ const pkgPath = path13.join(sub, "package.json");
4301
+ if (existsSync12(pkgPath)) {
4208
4302
  try {
4209
4303
  const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
4210
4304
  Object.assign(merged, pkg.dependencies ?? {}, pkg.devDependencies ?? {});
@@ -4223,8 +4317,8 @@ async function autoDetectStacksFromRoot(root) {
4223
4317
  let requirementsTxt;
4224
4318
  let goMod;
4225
4319
  let pomXml;
4226
- const pkgPath = path12.join(root, "package.json");
4227
- if (existsSync11(pkgPath)) {
4320
+ const pkgPath = path13.join(root, "package.json");
4321
+ if (existsSync12(pkgPath)) {
4228
4322
  try {
4229
4323
  const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
4230
4324
  packageJsonDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -4234,8 +4328,8 @@ async function autoDetectStacksFromRoot(root) {
4234
4328
  const nestedDeps = await collectNestedPackageDeps(root);
4235
4329
  if (nestedDeps) packageJsonDeps = { ...packageJsonDeps ?? {}, ...nestedDeps };
4236
4330
  for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
4237
- const reqPath = path12.join(root, name);
4238
- if (existsSync11(reqPath)) {
4331
+ const reqPath = path13.join(root, name);
4332
+ if (existsSync12(reqPath)) {
4239
4333
  try {
4240
4334
  requirementsTxt = await readFile6(reqPath, "utf8");
4241
4335
  break;
@@ -4243,39 +4337,39 @@ async function autoDetectStacksFromRoot(root) {
4243
4337
  }
4244
4338
  }
4245
4339
  }
4246
- const goModPath = path12.join(root, "go.mod");
4247
- if (existsSync11(goModPath)) {
4340
+ const goModPath = path13.join(root, "go.mod");
4341
+ if (existsSync12(goModPath)) {
4248
4342
  try {
4249
4343
  goMod = await readFile6(goModPath, "utf8");
4250
4344
  } catch {
4251
4345
  }
4252
4346
  }
4253
- const pomPath = path12.join(root, "pom.xml");
4254
- if (existsSync11(pomPath)) {
4347
+ const pomPath = path13.join(root, "pom.xml");
4348
+ if (existsSync12(pomPath)) {
4255
4349
  try {
4256
4350
  pomXml = await readFile6(pomPath, "utf8");
4257
4351
  } catch {
4258
4352
  }
4259
4353
  }
4260
4354
  let composerJson;
4261
- const composerPath = path12.join(root, "composer.json");
4262
- if (existsSync11(composerPath)) {
4355
+ const composerPath = path13.join(root, "composer.json");
4356
+ if (existsSync12(composerPath)) {
4263
4357
  try {
4264
4358
  composerJson = await readFile6(composerPath, "utf8");
4265
4359
  } catch {
4266
4360
  }
4267
4361
  }
4268
4362
  let gemfile;
4269
- const gemfilePath = path12.join(root, "Gemfile");
4270
- if (existsSync11(gemfilePath)) {
4363
+ const gemfilePath = path13.join(root, "Gemfile");
4364
+ if (existsSync12(gemfilePath)) {
4271
4365
  try {
4272
4366
  gemfile = await readFile6(gemfilePath, "utf8");
4273
4367
  } catch {
4274
4368
  }
4275
4369
  }
4276
- const hasDockerfile = existsSync11(path12.join(root, "Dockerfile"));
4277
- const hasTurboJson = existsSync11(path12.join(root, "turbo.json"));
4278
- const hasNxJson = existsSync11(path12.join(root, "nx.json"));
4370
+ const hasDockerfile = existsSync12(path13.join(root, "Dockerfile"));
4371
+ const hasTurboJson = existsSync12(path13.join(root, "turbo.json"));
4372
+ const hasNxJson = existsSync12(path13.join(root, "nx.json"));
4279
4373
  let hasCsproj = false;
4280
4374
  try {
4281
4375
  const entries = await readdir2(root);
@@ -4327,8 +4421,8 @@ async function seedFromGitHistory(root, paths, limit) {
4327
4421
  _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
4328
4422
  `;
4329
4423
  const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
4330
- if (existsSync11(file)) continue;
4331
- await mkdir6(path12.dirname(file), { recursive: true });
4424
+ if (existsSync12(file)) continue;
4425
+ await mkdir6(path13.dirname(file), { recursive: true });
4332
4426
  await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
4333
4427
  written++;
4334
4428
  }
@@ -4369,7 +4463,7 @@ function printInitReport(r) {
4369
4463
  lines.push(` Total ready : ${r.totalMemories} lesson(s), ${r.totalSensors} sensor(s) \u2014 0 written by hand`);
4370
4464
  }
4371
4465
  if (r.bridgesWritten > 0) {
4372
- lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (Cursor, Cline, Copilot, Roo, Gemini, \u2026)`);
4466
+ lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (${r.bridgeTargets.join(", ")})`);
4373
4467
  }
4374
4468
  if (lines.length === 0) return;
4375
4469
  const width = Math.max(...lines.map((l) => l.length), 44);
@@ -4390,33 +4484,37 @@ function printInitReport(r) {
4390
4484
  }
4391
4485
  async function writeCursorHaiveRule(root) {
4392
4486
  const relPath = ".cursor/rules/haive-mcp-required.mdc";
4393
- const target = path12.join(root, relPath);
4394
- if (existsSync11(target)) {
4487
+ const target = path13.join(root, relPath);
4488
+ if (existsSync12(target)) {
4395
4489
  ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
4396
4490
  return;
4397
4491
  }
4398
- await mkdir6(path12.dirname(target), { recursive: true });
4492
+ await mkdir6(path13.dirname(target), { recursive: true });
4399
4493
  await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
4400
4494
  ui.success(`Created Cursor rule ${relPath}`);
4401
4495
  }
4402
- function resolveBridgeTargets(opt) {
4403
- const raw = (opt ?? "all").trim().toLowerCase();
4404
- if (raw === "" || raw === "all") return [...BRIDGE_TARGETS];
4496
+ function resolveBridgeTargets(opt, root) {
4497
+ const raw = (opt ?? "auto").trim().toLowerCase();
4498
+ if (raw === "all") return [...BRIDGE_TARGETS2];
4499
+ if (raw === "" || raw === "auto") {
4500
+ const detection = detectBridgeTargets(root);
4501
+ const named = detection.targets.map((t) => detection.reasons[t] === "repo file" ? `${t} (already in repo)` : t).join(", ");
4502
+ ui.info(`Bridge targets (detected): ${named} \u2014 pass --bridge-targets all for every supported client`);
4503
+ return detection.targets;
4504
+ }
4405
4505
  const requested = raw.split(",").map((t) => t.trim()).filter(Boolean);
4406
- const valid = requested.filter((t) => BRIDGE_TARGETS.includes(t));
4407
- const invalid = requested.filter((t) => !BRIDGE_TARGETS.includes(t));
4506
+ const valid = requested.filter((t) => BRIDGE_TARGETS2.includes(t));
4507
+ const invalid = requested.filter((t) => !BRIDGE_TARGETS2.includes(t));
4408
4508
  if (invalid.length > 0) {
4409
- ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS.join(", ")}`);
4509
+ ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS2.join(", ")}`);
4410
4510
  }
4411
- return valid.length > 0 ? valid : [...BRIDGE_TARGETS];
4511
+ return valid.length > 0 ? valid : [...BRIDGE_TARGETS2];
4412
4512
  }
4413
4513
  var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
4414
4514
 
4415
4515
  Not team truth. Use for machine-local session notes or tooling scratch files.
4416
4516
  Official memories belong in .ai/memories/ (versioned in Git).
4417
4517
  Only .gitignore and this README are meant to commit; everything else stays untracked.
4418
-
4419
- Session continuity (local): agents may append \`session-journal.ndjson\` via MCP \`runtime_journal_append\` or \`hivelore runtime journal append\`.
4420
4518
  `;
4421
4519
  var RUNTIME_GITIGNORE_BODY = `*
4422
4520
  !.gitignore
@@ -4424,27 +4522,27 @@ var RUNTIME_GITIGNORE_BODY = `*
4424
4522
  `;
4425
4523
  async function ensureAiRuntimeLayout(runtimeDir) {
4426
4524
  await mkdir6(runtimeDir, { recursive: true });
4427
- const gi = path12.join(runtimeDir, ".gitignore");
4428
- if (!existsSync11(gi)) {
4525
+ const gi = path13.join(runtimeDir, ".gitignore");
4526
+ if (!existsSync12(gi)) {
4429
4527
  await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
4430
4528
  }
4431
- const readme = path12.join(runtimeDir, "README.md");
4432
- if (!existsSync11(readme)) {
4529
+ const readme = path13.join(runtimeDir, "README.md");
4530
+ if (!existsSync12(readme)) {
4433
4531
  await writeFile7(readme, RUNTIME_README_BODY, "utf8");
4434
4532
  }
4435
4533
  }
4436
4534
  async function ensureAiCacheLayout(cacheDir) {
4437
4535
  await mkdir6(cacheDir, { recursive: true });
4438
- const gi = path12.join(cacheDir, ".gitignore");
4439
- if (!existsSync11(gi)) {
4536
+ const gi = path13.join(cacheDir, ".gitignore");
4537
+ if (!existsSync12(gi)) {
4440
4538
  await writeFile7(gi, "*\n!.gitignore\n", "utf8");
4441
4539
  }
4442
4540
  }
4443
4541
  async function ensureGitignoreEntries(root, patterns) {
4444
4542
  try {
4445
- const gitignorePath = path12.join(root, ".gitignore");
4543
+ const gitignorePath = path13.join(root, ".gitignore");
4446
4544
  let existing = "";
4447
- if (existsSync11(gitignorePath)) {
4545
+ if (existsSync12(gitignorePath)) {
4448
4546
  existing = await readFile6(gitignorePath, "utf8");
4449
4547
  }
4450
4548
  const lines = existing.split("\n");
@@ -4458,8 +4556,8 @@ async function ensureGitignoreEntries(root, patterns) {
4458
4556
 
4459
4557
  // src/commands/observe.ts
4460
4558
  import { appendFile, mkdir as mkdir7 } from "fs/promises";
4461
- import { existsSync as existsSync12 } from "fs";
4462
- import path13 from "path";
4559
+ import { existsSync as existsSync13 } from "fs";
4560
+ import path14 from "path";
4463
4561
  import "commander";
4464
4562
  import { findProjectRoot as findProjectRoot7, resolveHaivePaths as resolveHaivePaths7 } from "@hivelore/core";
4465
4563
  var MAX_STDIN_BYTES = 256 * 1024;
@@ -4566,7 +4664,7 @@ function registerObserve(program2) {
4566
4664
  })();
4567
4665
  if (!root) return;
4568
4666
  const paths = resolveHaivePaths7(root);
4569
- if (!existsSync12(paths.haiveDir)) return;
4667
+ if (!existsSync13(paths.haiveDir)) return;
4570
4668
  const failureHint = detectFailure(payload);
4571
4669
  const observation = {
4572
4670
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4577,10 +4675,10 @@ function registerObserve(program2) {
4577
4675
  files: extractFiles(payload),
4578
4676
  ...failureHint ? { failure_hint: true } : {}
4579
4677
  };
4580
- const cacheDir = path13.join(paths.haiveDir, ".cache");
4678
+ const cacheDir = path14.join(paths.haiveDir, ".cache");
4581
4679
  await mkdir7(cacheDir, { recursive: true });
4582
4680
  await appendFile(
4583
- path13.join(cacheDir, "observations.jsonl"),
4681
+ path14.join(cacheDir, "observations.jsonl"),
4584
4682
  JSON.stringify(observation) + "\n",
4585
4683
  "utf8"
4586
4684
  );
@@ -4611,30 +4709,37 @@ function registerMcp(program2) {
4611
4709
  // src/commands/sync.ts
4612
4710
  import { spawnSync as spawnSync4 } from "child_process";
4613
4711
  import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
4614
- import { existsSync as existsSync13 } from "fs";
4615
- import path14 from "path";
4712
+ import { existsSync as existsSync14 } from "fs";
4713
+ import path15 from "path";
4616
4714
  import "commander";
4617
4715
  import {
4618
4716
  DEFAULT_AUTO_PROMOTE_RULE,
4717
+ assessSensorHealth,
4619
4718
  buildFrontmatter as buildFrontmatter3,
4719
+ existingGateMissShas,
4620
4720
  findProjectRoot as findProjectRoot9,
4621
4721
  getUsage as getUsage2,
4722
+ gatePassedShas,
4622
4723
  isAutoPromoteEligible,
4623
4724
  isDecaying,
4624
4725
  isStackPackSeed,
4625
4726
  loadCodeMap as loadCodeMap5,
4626
4727
  loadConfig as loadConfig3,
4627
4728
  loadMemoriesFromDir as loadMemoriesFromDir7,
4729
+ loadSensorLedger,
4628
4730
  loadUsageIndex as loadUsageIndex3,
4629
4731
  pullCrossRepoSources,
4732
+ planGitWatch,
4733
+ proposeGateMissDrafts,
4630
4734
  resolveHaivePaths as resolveHaivePaths8,
4631
4735
  resolveManifestFiles,
4632
4736
  serializeMemory as serializeMemory4,
4737
+ withQuarantineNote,
4633
4738
  trackDependencies,
4634
4739
  verifyAnchor,
4635
4740
  watchContracts
4636
4741
  } from "@hivelore/core";
4637
- import { BRIDGE_TARGETS as BRIDGE_TARGETS2 } from "@hivelore/core";
4742
+ import { BRIDGE_TARGETS as BRIDGE_TARGETS3 } from "@hivelore/core";
4638
4743
  var BRIDGE_START = "<!-- haive:memories-start -->";
4639
4744
  var BRIDGE_END = "<!-- haive:memories-end -->";
4640
4745
  function registerSync(program2) {
@@ -4649,7 +4754,7 @@ function registerSync(program2) {
4649
4754
  ).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").option("--no-bridges", "skip auto-refresh of existing native agent bridge files (.cursor/rules, .clinerules, \u2026)").option("--dry-run", "report what would change without writing any files").action(async (opts) => {
4650
4755
  const root = findProjectRoot9(opts.dir);
4651
4756
  const paths = resolveHaivePaths8(root);
4652
- if (!existsSync13(paths.memoriesDir)) {
4757
+ if (!existsSync14(paths.memoriesDir)) {
4653
4758
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
4654
4759
  process.exitCode = 1;
4655
4760
  return;
@@ -4667,6 +4772,24 @@ function registerSync(program2) {
4667
4772
  let revalidated = 0;
4668
4773
  let promoted = 0;
4669
4774
  let autoApproved = 0;
4775
+ let quarantined = 0;
4776
+ const sensorHealth = new Map(
4777
+ assessSensorHealth(await loadSensorLedger(paths)).map((health) => [health.memory_id, health])
4778
+ );
4779
+ const quarantineCandidates = await loadMemoriesFromDir7(paths.memoriesDir);
4780
+ for (const { memory: memory2, filePath } of quarantineCandidates) {
4781
+ const sensor = memory2.frontmatter.sensor;
4782
+ const health = sensorHealth.get(memory2.frontmatter.id);
4783
+ if (!sensor || sensor.kind !== "shell" && sensor.kind !== "test" || sensor.severity !== "block" || !health?.quarantine_pending) continue;
4784
+ if (!dryRun) {
4785
+ const at = (/* @__PURE__ */ new Date()).toISOString();
4786
+ await writeFile8(filePath, serializeMemory4({
4787
+ frontmatter: { ...memory2.frontmatter, sensor: { ...sensor, severity: "warn" } },
4788
+ body: withQuarantineNote(memory2.body, at, health.flap_count)
4789
+ }), "utf8");
4790
+ }
4791
+ quarantined++;
4792
+ }
4670
4793
  if (opts.verify !== false) {
4671
4794
  const memories = await loadMemoriesFromDir7(paths.memoriesDir);
4672
4795
  for (const { memory: memory2, filePath } of memories) {
@@ -4787,13 +4910,17 @@ function registerSync(program2) {
4787
4910
  for (const repair of repairs) log(ui.dim(`autopilot: ${repair.message}`));
4788
4911
  }
4789
4912
  const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
4913
+ const gateMissIds = await processGateMissWatch(root, paths, dryRun);
4914
+ if (gateMissIds.length > 0) {
4915
+ log(ui.yellow(`gate-miss: proposed ${gateMissIds.length} lesson(s): ${gateMissIds.join(", ")}`));
4916
+ }
4790
4917
  const draftMemories = (await loadMemoriesFromDir7(paths.memoriesDir)).filter(
4791
4918
  (m) => m.memory.frontmatter.status === "draft"
4792
4919
  );
4793
4920
  const draftCount = draftMemories.length;
4794
4921
  const autoApprovedNote = autoApproved > 0 ? ` \xB7 ${autoApproved} auto-approved` : "";
4795
4922
  log(
4796
- `${ui.dim("sync:")} ${staleMarked} stale \xB7 ${revalidated} revalidated \xB7 ${promoted} promoted${autoApprovedNote}${sinceReport ? ` \xB7 ${sinceReport.added.length}+/${sinceReport.modified.length}~/${sinceReport.removed.length}- since ${opts.since}` : ""}`
4923
+ `${ui.dim("sync:")} ${staleMarked} stale \xB7 ${revalidated} revalidated \xB7 ${promoted} promoted \xB7 ${quarantined} quarantined${autoApprovedNote}${sinceReport ? ` \xB7 ${sinceReport.added.length}+/${sinceReport.modified.length}~/${sinceReport.removed.length}- since ${opts.since}` : ""}`
4797
4924
  );
4798
4925
  if (!opts.quiet && draftCount > 0) {
4799
4926
  log(
@@ -4805,7 +4932,7 @@ function registerSync(program2) {
4805
4932
  if (opts.injectBridge) {
4806
4933
  const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
4807
4934
  if (opts.bridgeFile) {
4808
- await injectBridge(path14.resolve(opts.bridgeFile), paths.memoriesDir, maxInject, root, opts.quiet);
4935
+ await injectBridge(path15.resolve(opts.bridgeFile), paths.memoriesDir, maxInject, root, opts.quiet);
4809
4936
  } else if (!dryRun) {
4810
4937
  const res = await writeBridgeFiles(root, paths, {
4811
4938
  targets: ["claude", "agents"],
@@ -4834,7 +4961,7 @@ function registerSync(program2) {
4834
4961
  if (opts.noBridges !== true) {
4835
4962
  try {
4836
4963
  const res = await writeBridgeFiles(root, paths, {
4837
- targets: BRIDGE_TARGETS2,
4964
+ targets: BRIDGE_TARGETS3,
4838
4965
  onlyExisting: true,
4839
4966
  dryRun
4840
4967
  });
@@ -4954,10 +5081,10 @@ Wait for **explicit confirmation** before acting.
4954
5081
  topic: `dep-bump-${slugParts}`
4955
5082
  });
4956
5083
  if (!dryRun) {
4957
- const teamDir = path14.join(paths.memoriesDir, "team");
5084
+ const teamDir = path15.join(paths.memoriesDir, "team");
4958
5085
  await mkdir8(teamDir, { recursive: true });
4959
5086
  await writeFile8(
4960
- path14.join(teamDir, `${fm.id}.md`),
5087
+ path15.join(teamDir, `${fm.id}.md`),
4961
5088
  serializeMemory4({ frontmatter: { ...fm, requires_human_approval: true }, body }),
4962
5089
  "utf8"
4963
5090
  );
@@ -5023,10 +5150,10 @@ Wait for **explicit confirmation** before acting.
5023
5150
  topic: `contract-breaking-${diff.contract}`
5024
5151
  });
5025
5152
  if (!dryRun) {
5026
- const teamDir = path14.join(paths.memoriesDir, "team");
5153
+ const teamDir = path15.join(paths.memoriesDir, "team");
5027
5154
  await mkdir8(teamDir, { recursive: true });
5028
5155
  await writeFile8(
5029
- path14.join(teamDir, `${fm.id}.md`),
5156
+ path15.join(teamDir, `${fm.id}.md`),
5030
5157
  serializeMemory4({ frontmatter: { ...fm, requires_human_approval: true }, body }),
5031
5158
  "utf8"
5032
5159
  );
@@ -5111,13 +5238,90 @@ Wait for **explicit confirmation** before acting.
5111
5238
  }
5112
5239
  });
5113
5240
  }
5241
+ async function processGateMissWatch(root, paths, dryRun) {
5242
+ const headResult = spawnSync4("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" });
5243
+ if (headResult.status !== 0) return [];
5244
+ const head = headResult.stdout.trim();
5245
+ if (!head) return [];
5246
+ const stateFile = path15.join(paths.runtimeDir, "enforcement", "git-watch.json");
5247
+ let state = null;
5248
+ try {
5249
+ state = JSON.parse(await readFile7(stateFile, "utf8"));
5250
+ } catch {
5251
+ }
5252
+ const plan = planGitWatch(state, head);
5253
+ if (plan.action === "initialize") {
5254
+ if (!dryRun) {
5255
+ await mkdir8(path15.dirname(stateFile), { recursive: true });
5256
+ await writeFile8(stateFile, JSON.stringify(plan.next, null, 2) + "\n", "utf8");
5257
+ }
5258
+ return [];
5259
+ }
5260
+ if (plan.action === "idle") return [];
5261
+ const proposals = proposeGateMissDrafts(
5262
+ readGitCommitsRange(root, plan.range),
5263
+ existingGateMissShas(await loadMemoriesFromDir7(paths.memoriesDir)),
5264
+ gatePassedShas(await loadSensorLedger(paths))
5265
+ );
5266
+ const ids = [];
5267
+ for (const proposal of proposals) {
5268
+ const fm = {
5269
+ ...buildFrontmatter3({
5270
+ type: "attempt",
5271
+ slug: proposal.slug,
5272
+ scope: "team",
5273
+ status: "proposed",
5274
+ tags: ["gate-miss", proposal.kind],
5275
+ paths: proposal.paths,
5276
+ topic: `gate-miss-${proposal.reverted_sha}`
5277
+ }),
5278
+ status: "proposed"
5279
+ };
5280
+ ids.push(fm.id);
5281
+ if (!dryRun) {
5282
+ await mkdir8(paths.teamDir, { recursive: true });
5283
+ await writeFile8(
5284
+ path15.join(paths.teamDir, `${fm.id}.md`),
5285
+ serializeMemory4({ frontmatter: fm, body: proposal.body }),
5286
+ "utf8"
5287
+ );
5288
+ }
5289
+ }
5290
+ if (!dryRun) {
5291
+ await mkdir8(path15.dirname(stateFile), { recursive: true });
5292
+ await writeFile8(stateFile, JSON.stringify(plan.next, null, 2) + "\n", "utf8");
5293
+ }
5294
+ return ids;
5295
+ }
5296
+ function readGitCommitsRange(root, range) {
5297
+ const result = spawnSync4(
5298
+ "git",
5299
+ ["log", "--reverse", "--format=%x1e%H%x1f%s%x1f%b%x1f", "--name-only", range],
5300
+ { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }
5301
+ );
5302
+ if (result.status !== 0) return [];
5303
+ const commits = [];
5304
+ for (const block of result.stdout.split("").filter((part) => part.trim())) {
5305
+ const [shaRaw, subjectRaw, bodyRaw, filesRaw = ""] = block.split("");
5306
+ const sha = shaRaw?.trim();
5307
+ const subject = subjectRaw?.trim();
5308
+ if (!sha || !subject) continue;
5309
+ commits.push({
5310
+ sha,
5311
+ subject,
5312
+ body: bodyRaw?.trim() ?? "",
5313
+ files: filesRaw.split("\n").map((file) => file.trim()).filter(Boolean)
5314
+ });
5315
+ }
5316
+ return commits;
5317
+ }
5114
5318
  function bridgeSummaryLine(body) {
5115
5319
  const firstLine = body.split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l.length > 0) ?? "";
5116
5320
  const oneLine = firstLine.replace(/\s+/g, " ");
5117
5321
  return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
5118
5322
  }
5119
5323
  async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
5120
- if (!existsSync13(memoriesDir)) return;
5324
+ if (!existsSync14(memoriesDir)) return;
5121
5325
  const all = await loadMemoriesFromDir7(memoriesDir);
5122
5326
  const top = all.filter(({ memory: memory2 }) => {
5123
5327
  const s = memory2.frontmatter.status;
@@ -5143,17 +5347,17 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
5143
5347
  ` + block + `
5144
5348
 
5145
5349
  ${BRIDGE_END}`;
5146
- const fileExists = existsSync13(bridgeFile);
5350
+ const fileExists = existsSync14(bridgeFile);
5147
5351
  let existing = fileExists ? await readFile7(bridgeFile, "utf8") : "";
5148
5352
  existing = existing.replace(/\r\n/g, "\n");
5149
5353
  const startIdx = existing.indexOf(BRIDGE_START);
5150
5354
  const endIdx = existing.indexOf(BRIDGE_END);
5151
5355
  if (startIdx !== -1 && endIdx === -1) {
5152
- ui.warn(`${path14.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
5356
+ ui.warn(`${path15.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
5153
5357
  return;
5154
5358
  }
5155
5359
  if (startIdx === -1 && endIdx !== -1) {
5156
- ui.warn(`${path14.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
5360
+ ui.warn(`${path15.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
5157
5361
  return;
5158
5362
  }
5159
5363
  let updated;
@@ -5161,14 +5365,14 @@ ${BRIDGE_END}`;
5161
5365
  updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
5162
5366
  } else {
5163
5367
  if (!fileExists && !quiet) {
5164
- ui.info(`Creating ${path14.relative(root, bridgeFile)} with hivelore memory block.`);
5368
+ ui.info(`Creating ${path15.relative(root, bridgeFile)} with hivelore memory block.`);
5165
5369
  }
5166
5370
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
5167
5371
  }
5168
5372
  await writeFile8(bridgeFile, updated, "utf8");
5169
5373
  if (!quiet) {
5170
5374
  console.log(
5171
- ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path14.relative(root, bridgeFile)}`)
5375
+ ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path15.relative(root, bridgeFile)}`)
5172
5376
  );
5173
5377
  }
5174
5378
  }
@@ -5194,8 +5398,8 @@ function collectSinceChanges(root, ref) {
5194
5398
  // src/commands/memory-add.ts
5195
5399
  import { createHash } from "crypto";
5196
5400
  import { mkdir as mkdir9, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
5197
- import { existsSync as existsSync14 } from "fs";
5198
- import path15 from "path";
5401
+ import { existsSync as existsSync15 } from "fs";
5402
+ import path16 from "path";
5199
5403
  import { Option } from "commander";
5200
5404
  import {
5201
5405
  buildFrontmatter as buildFrontmatter4,
@@ -5237,7 +5441,7 @@ function registerMemoryAdd(memory2) {
5237
5441
  if (opts.body === void 0 && opts.content !== void 0) opts.body = opts.content;
5238
5442
  const root = findProjectRoot10(opts.dir);
5239
5443
  const paths = resolveHaivePaths9(root);
5240
- if (!existsSync14(paths.haiveDir)) {
5444
+ if (!existsSync15(paths.haiveDir)) {
5241
5445
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
5242
5446
  process.exitCode = 1;
5243
5447
  return;
@@ -5254,7 +5458,7 @@ function registerMemoryAdd(memory2) {
5254
5458
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths2(anchorPaths) : [];
5255
5459
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
5256
5460
  if (anchorPaths.length > 0) {
5257
- const missing = anchorPaths.filter((p) => !existsSync14(path15.resolve(root, p)));
5461
+ const missing = anchorPaths.filter((p) => !existsSync15(path16.resolve(root, p)));
5258
5462
  if (missing.length > 0) {
5259
5463
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
5260
5464
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -5267,7 +5471,7 @@ function registerMemoryAdd(memory2) {
5267
5471
  const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
5268
5472
  let body;
5269
5473
  if (opts.bodyFile !== void 0) {
5270
- if (!existsSync14(opts.bodyFile)) {
5474
+ if (!existsSync15(opts.bodyFile)) {
5271
5475
  ui.error(`--body-file not found: ${opts.bodyFile}`);
5272
5476
  process.exitCode = 1;
5273
5477
  return;
@@ -5283,7 +5487,7 @@ TODO \u2014 write the memory body.
5283
5487
  `;
5284
5488
  }
5285
5489
  const scope = opts.scope ?? config.defaultScope ?? "personal";
5286
- if (existsSync14(paths.memoriesDir)) {
5490
+ if (existsSync15(paths.memoriesDir)) {
5287
5491
  const incomingHash = createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
5288
5492
  const allForHash = await loadMemoriesFromDir8(paths.memoriesDir);
5289
5493
  const hashDup = allForHash.find(
@@ -5296,7 +5500,7 @@ TODO \u2014 write the memory body.
5296
5500
  return;
5297
5501
  }
5298
5502
  }
5299
- if (opts.topic && existsSync14(paths.memoriesDir)) {
5503
+ if (opts.topic && existsSync15(paths.memoriesDir)) {
5300
5504
  const existing = await loadMemoriesFromDir8(paths.memoriesDir);
5301
5505
  const topicMatch = existing.find(
5302
5506
  ({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
@@ -5316,7 +5520,7 @@ TODO \u2014 write the memory body.
5316
5520
  }
5317
5521
  };
5318
5522
  await writeFile9(topicMatch.filePath, serializeMemory5({ frontmatter: newFrontmatter, body }), "utf8");
5319
- ui.success(`Updated (topic upsert) ${path15.relative(root, topicMatch.filePath)}`);
5523
+ ui.success(`Updated (topic upsert) ${path16.relative(root, topicMatch.filePath)}`);
5320
5524
  ui.info(`id=${fm.id} revision=${revisionCount}`);
5321
5525
  printSensorLoopHint(opts.type, body, newFrontmatter.anchor.paths, Boolean(newFrontmatter.sensor));
5322
5526
  await runPostMemoryAutopilot(root, paths, config);
@@ -5340,13 +5544,13 @@ TODO \u2014 write the memory body.
5340
5544
  });
5341
5545
  if (frontmatter.status === "validated") frontmatter.validated_by = "auto";
5342
5546
  const file = memoryFilePath3(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
5343
- await mkdir9(path15.dirname(file), { recursive: true });
5344
- if (existsSync14(file)) {
5547
+ await mkdir9(path16.dirname(file), { recursive: true });
5548
+ if (existsSync15(file)) {
5345
5549
  ui.error(`Memory already exists at ${file}`);
5346
5550
  process.exitCode = 1;
5347
5551
  return;
5348
5552
  }
5349
- if (existsSync14(paths.memoriesDir)) {
5553
+ if (existsSync15(paths.memoriesDir)) {
5350
5554
  const existing = await loadMemoriesFromDir8(paths.memoriesDir);
5351
5555
  const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
5352
5556
  const similar = existing.filter(({ memory: memory3 }) => {
@@ -5359,7 +5563,7 @@ TODO \u2014 write the memory body.
5359
5563
  }
5360
5564
  }
5361
5565
  await writeFile9(file, serializeMemory5({ frontmatter, body }), "utf8");
5362
- ui.success(`Created ${path15.relative(root, file)}`);
5566
+ ui.success(`Created ${path16.relative(root, file)}`);
5363
5567
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
5364
5568
  printSensorLoopHint(opts.type, body, anchorPaths, Boolean(frontmatter.sensor));
5365
5569
  await runPostMemoryAutopilot(root, paths, config);
@@ -5453,8 +5657,8 @@ function slugify(value) {
5453
5657
  }
5454
5658
 
5455
5659
  // src/commands/memory-list.ts
5456
- import { existsSync as existsSync15 } from "fs";
5457
- import path16 from "path";
5660
+ import { existsSync as existsSync16 } from "fs";
5661
+ import path17 from "path";
5458
5662
  import "commander";
5459
5663
  import { findProjectRoot as findProjectRoot11, resolveHaivePaths as resolveHaivePaths10 } from "@hivelore/core";
5460
5664
  function registerMemoryList(memory2) {
@@ -5466,7 +5670,7 @@ function registerMemoryList(memory2) {
5466
5670
  }
5467
5671
  const root = findProjectRoot11(opts.dir);
5468
5672
  const paths = resolveHaivePaths10(root);
5469
- if (!existsSync15(paths.memoriesDir)) {
5673
+ if (!existsSync16(paths.memoriesDir)) {
5470
5674
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
5471
5675
  process.exitCode = 1;
5472
5676
  return;
@@ -5505,7 +5709,7 @@ function registerMemoryList(memory2) {
5505
5709
  );
5506
5710
  const title = mem.body.match(/^#\s+(.+)$/m)?.[1]?.trim();
5507
5711
  if (title && title !== fm.id) console.log(` ${title}`);
5508
- console.log(` ${ui.dim(path16.relative(root, filePath))}`);
5712
+ console.log(` ${ui.dim(path17.relative(root, filePath))}`);
5509
5713
  }
5510
5714
  const totalLabel = clipped > 0 ? `
5511
5715
  ${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
@@ -5543,8 +5747,8 @@ function matchesFilters(loaded, opts) {
5543
5747
 
5544
5748
  // src/commands/memory-promote.ts
5545
5749
  import { mkdir as mkdir10, unlink, writeFile as writeFile10 } from "fs/promises";
5546
- import { existsSync as existsSync16 } from "fs";
5547
- import path17 from "path";
5750
+ import { existsSync as existsSync17 } from "fs";
5751
+ import path18 from "path";
5548
5752
  import "commander";
5549
5753
  import {
5550
5754
  findProjectRoot as findProjectRoot12,
@@ -5556,7 +5760,7 @@ function registerMemoryPromote(memory2) {
5556
5760
  memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
5557
5761
  const root = findProjectRoot12(opts.dir);
5558
5762
  const paths = resolveHaivePaths11(root);
5559
- if (!existsSync16(paths.memoriesDir)) {
5763
+ if (!existsSync17(paths.memoriesDir)) {
5560
5764
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
5561
5765
  process.exitCode = 1;
5562
5766
  return;
@@ -5591,19 +5795,19 @@ function registerMemoryPromote(memory2) {
5591
5795
  body: found.memory.body
5592
5796
  };
5593
5797
  const newPath = memoryFilePath4(paths, "team", updated.frontmatter.id);
5594
- await mkdir10(path17.dirname(newPath), { recursive: true });
5798
+ await mkdir10(path18.dirname(newPath), { recursive: true });
5595
5799
  await writeFile10(newPath, serializeMemory6(updated), "utf8");
5596
5800
  await unlink(found.filePath);
5597
5801
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
5598
- ui.info(`Now at ${path17.relative(root, newPath)}`);
5802
+ ui.info(`Now at ${path18.relative(root, newPath)}`);
5599
5803
  console.log(ui.dim(`\u2192 next: hivelore memory approve ${id} (validate for team use)`));
5600
5804
  });
5601
5805
  }
5602
5806
 
5603
5807
  // src/commands/memory-approve.ts
5604
- import { existsSync as existsSync17 } from "fs";
5808
+ import { existsSync as existsSync18 } from "fs";
5605
5809
  import { writeFile as writeFile11 } from "fs/promises";
5606
- import path18 from "path";
5810
+ import path19 from "path";
5607
5811
  import "commander";
5608
5812
  import {
5609
5813
  findProjectRoot as findProjectRoot13,
@@ -5614,7 +5818,7 @@ function registerMemoryApprove(memory2) {
5614
5818
  memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
5615
5819
  const root = findProjectRoot13(opts.dir);
5616
5820
  const paths = resolveHaivePaths12(root);
5617
- if (!existsSync17(paths.memoriesDir)) {
5821
+ if (!existsSync18(paths.memoriesDir)) {
5618
5822
  ui.error(`No .ai/memories at ${root}.`);
5619
5823
  process.exitCode = 1;
5620
5824
  return;
@@ -5668,15 +5872,15 @@ function registerMemoryApprove(memory2) {
5668
5872
  };
5669
5873
  await writeFile11(found.filePath, serializeMemory7(next), "utf8");
5670
5874
  ui.success(`Approved ${id} (status=validated, by=human)`);
5671
- ui.info(path18.relative(root, found.filePath));
5875
+ ui.info(path19.relative(root, found.filePath));
5672
5876
  });
5673
5877
  }
5674
5878
 
5675
5879
  // src/commands/memory-update.ts
5676
5880
  import { spawn } from "child_process";
5677
5881
  import { readFile as readFile9, writeFile as writeFile12 } from "fs/promises";
5678
- import { existsSync as existsSync18 } from "fs";
5679
- import path19 from "path";
5882
+ import { existsSync as existsSync19 } from "fs";
5883
+ import path20 from "path";
5680
5884
  import "commander";
5681
5885
  import {
5682
5886
  findProjectRoot as findProjectRoot14,
@@ -5689,7 +5893,7 @@ function registerMemoryUpdate(memory2) {
5689
5893
  const root = findProjectRoot14(opts.dir);
5690
5894
  const paths = resolveHaivePaths13(root);
5691
5895
  if (opts.edit) {
5692
- const all = existsSync18(paths.memoriesDir) ? await loadMemoriesFromDir(paths.memoriesDir) : [];
5896
+ const all = existsSync19(paths.memoriesDir) ? await loadMemoriesFromDir(paths.memoriesDir) : [];
5693
5897
  const found = all.find((m) => m.memory.frontmatter.id === id);
5694
5898
  if (!found) {
5695
5899
  ui.error(`No memory with id "${id}".`);
@@ -5697,7 +5901,7 @@ function registerMemoryUpdate(memory2) {
5697
5901
  return;
5698
5902
  }
5699
5903
  const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
5700
- ui.info(`Opening ${path19.relative(root, found.filePath)} with ${editor}\u2026`);
5904
+ ui.info(`Opening ${path20.relative(root, found.filePath)} with ${editor}\u2026`);
5701
5905
  const code = await new Promise((resolve) => {
5702
5906
  const child = spawn(editor, [found.filePath], { stdio: "inherit" });
5703
5907
  child.on("exit", (c) => resolve(c ?? 0));
@@ -5714,7 +5918,7 @@ function registerMemoryUpdate(memory2) {
5714
5918
  }
5715
5919
  return;
5716
5920
  }
5717
- if (!existsSync18(paths.memoriesDir)) {
5921
+ if (!existsSync19(paths.memoriesDir)) {
5718
5922
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
5719
5923
  process.exitCode = 1;
5720
5924
  return;
@@ -5756,7 +5960,7 @@ function registerMemoryUpdate(memory2) {
5756
5960
  if (opts.author !== void 0) updated.push("author");
5757
5961
  let newBody;
5758
5962
  if (opts.bodyFile !== void 0) {
5759
- if (!existsSync18(opts.bodyFile)) {
5963
+ if (!existsSync19(opts.bodyFile)) {
5760
5964
  ui.error(`--body-file not found: ${opts.bodyFile}`);
5761
5965
  process.exitCode = 1;
5762
5966
  return;
@@ -5782,7 +5986,7 @@ function registerMemoryUpdate(memory2) {
5782
5986
  serializeMemory8({ frontmatter: newFrontmatter, body: newBody }),
5783
5987
  "utf8"
5784
5988
  );
5785
- ui.success(`Updated ${path19.relative(root, loaded.filePath)}`);
5989
+ ui.success(`Updated ${path20.relative(root, loaded.filePath)}`);
5786
5990
  ui.info(`fields: ${updated.join(", ")}`);
5787
5991
  });
5788
5992
  }
@@ -5801,8 +6005,8 @@ function parseCsv3(value) {
5801
6005
  }
5802
6006
 
5803
6007
  // src/commands/memory-tried.ts
5804
- import { existsSync as existsSync19 } from "fs";
5805
- import path20 from "path";
6008
+ import { existsSync as existsSync20 } from "fs";
6009
+ import path21 from "path";
5806
6010
  import "commander";
5807
6011
  import {
5808
6012
  findProjectRoot as findProjectRoot15,
@@ -5817,7 +6021,7 @@ function registerMemoryTried(memory2) {
5817
6021
  ).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("--sensor-pattern <regex>", "one-shot: regex matching the FAULTY usage \u2014 validates + attaches a sensor in this call").option("--sensor-command <cmd>", "one-shot BEHAVIOUR sensor: a command (test/script) the gate runs when the diff touches --paths; non-zero exit = lesson fires").option("--sensor-kind <kind>", "with --sensor-command: shell | test (default test)").option("--sensor-timeout <ms>", "with --sensor-command: max runtime in ms (default 120000)").option("--sensor-absent <regex>", "one-shot: regex marking CORRECT usage nearby (excludes it from firing)").option("--sensor-severity <level>", "one-shot sensor severity: warn | block", "block").option("--sensor-message <text>", "one-shot: self-correction message shown when the sensor fires").option("--bad-example <code>", "one-shot: code snippet the sensor must fire on (validation)").option("-d, --dir <dir>", "project root").action(async (opts) => {
5818
6022
  const root = findProjectRoot15(opts.dir);
5819
6023
  const paths = resolveHaivePaths14(root);
5820
- if (!existsSync19(paths.haiveDir)) {
6024
+ if (!existsSync20(paths.haiveDir)) {
5821
6025
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
5822
6026
  process.exitCode = 1;
5823
6027
  return;
@@ -5863,7 +6067,7 @@ function registerMemoryTried(memory2) {
5863
6067
  process.exitCode = 1;
5864
6068
  return;
5865
6069
  }
5866
- ui.success(`Recorded: ${path20.relative(root, result.file_path)}`);
6070
+ ui.success(`Recorded: ${path21.relative(root, result.file_path)}`);
5867
6071
  ui.info(`id=${result.id} type=attempt status=validated (auto-approved)`);
5868
6072
  if (result.sensor_result) {
5869
6073
  if (result.sensor_result.accepted) {
@@ -5887,8 +6091,8 @@ function registerMemoryTried(memory2) {
5887
6091
 
5888
6092
  // src/commands/memory-seed.ts
5889
6093
  import { readFile as readFile10 } from "fs/promises";
5890
- import { existsSync as existsSync20 } from "fs";
5891
- import path21 from "path";
6094
+ import { existsSync as existsSync21 } from "fs";
6095
+ import path22 from "path";
5892
6096
  import "commander";
5893
6097
  import {
5894
6098
  findProjectRoot as findProjectRoot16,
@@ -5897,7 +6101,7 @@ import {
5897
6101
  } from "@hivelore/core";
5898
6102
  async function readDependencyMap(root) {
5899
6103
  try {
5900
- const raw = await readFile10(path21.join(root, "package.json"), "utf8");
6104
+ const raw = await readFile10(path22.join(root, "package.json"), "utf8");
5901
6105
  const pkg = JSON.parse(raw);
5902
6106
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
5903
6107
  } catch {
@@ -5932,7 +6136,7 @@ function registerMemorySeed(memory2) {
5932
6136
  }
5933
6137
  return;
5934
6138
  }
5935
- if (!existsSync20(paths.haiveDir)) {
6139
+ if (!existsSync21(paths.haiveDir)) {
5936
6140
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
5937
6141
  process.exitCode = 1;
5938
6142
  return;
@@ -5988,8 +6192,8 @@ function registerMemorySeed(memory2) {
5988
6192
  }
5989
6193
 
5990
6194
  // src/commands/memory-query.ts
5991
- import { existsSync as existsSync21 } from "fs";
5992
- import path22 from "path";
6195
+ import { existsSync as existsSync22 } from "fs";
6196
+ import path23 from "path";
5993
6197
  import "commander";
5994
6198
  import {
5995
6199
  extractSnippet,
@@ -6005,7 +6209,7 @@ function registerMemoryQuery(memory2) {
6005
6209
  memory2.command("search <text>").alias("query").description("Search memories by id, tag, or substring (AND, OR fallback). Mirrors MCP mem_search. Alias: query").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
6006
6210
  const root = findProjectRoot17(opts.dir);
6007
6211
  const paths = resolveHaivePaths16(root);
6008
- if (!existsSync21(paths.memoriesDir)) {
6212
+ if (!existsSync22(paths.memoriesDir)) {
6009
6213
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
6010
6214
  process.exitCode = 1;
6011
6215
  return;
@@ -6046,7 +6250,7 @@ function registerMemoryQuery(memory2) {
6046
6250
  const fm = mem.frontmatter;
6047
6251
  const statusBadge = ui.statusBadge(fm.status);
6048
6252
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
6049
- console.log(` ${ui.dim(path22.relative(root, filePath))}`);
6253
+ console.log(` ${ui.dim(path23.relative(root, filePath))}`);
6050
6254
  const snippet = extractSnippet(mem.body, snippetNeedle);
6051
6255
  if (snippet) console.log(` ${snippet}`);
6052
6256
  }
@@ -6064,7 +6268,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
6064
6268
 
6065
6269
  // src/commands/memory-reject.ts
6066
6270
  import { writeFile as writeFile13 } from "fs/promises";
6067
- import { existsSync as existsSync22 } from "fs";
6271
+ import { existsSync as existsSync23 } from "fs";
6068
6272
  import "commander";
6069
6273
  import {
6070
6274
  findProjectRoot as findProjectRoot18,
@@ -6078,7 +6282,7 @@ function registerMemoryReject(memory2) {
6078
6282
  memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
6079
6283
  const root = findProjectRoot18(opts.dir);
6080
6284
  const paths = resolveHaivePaths17(root);
6081
- if (!existsSync22(paths.memoriesDir)) {
6285
+ if (!existsSync23(paths.memoriesDir)) {
6082
6286
  ui.error(`No .ai/memories at ${root}.`);
6083
6287
  process.exitCode = 1;
6084
6288
  return;
@@ -6114,9 +6318,9 @@ function registerMemoryReject(memory2) {
6114
6318
  }
6115
6319
 
6116
6320
  // src/commands/memory-rm.ts
6117
- import { existsSync as existsSync23 } from "fs";
6321
+ import { existsSync as existsSync24 } from "fs";
6118
6322
  import { unlink as unlink2 } from "fs/promises";
6119
- import path23 from "path";
6323
+ import path24 from "path";
6120
6324
  import { createInterface as createInterface2 } from "readline/promises";
6121
6325
  import "commander";
6122
6326
  import {
@@ -6129,7 +6333,7 @@ function registerMemoryRm(memory2) {
6129
6333
  memory2.command("delete <id>").alias("rm").description("Delete a memory file (and its usage entry by default). Mirrors MCP mem_delete. Alias: rm").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
6130
6334
  const root = findProjectRoot19(opts.dir);
6131
6335
  const paths = resolveHaivePaths18(root);
6132
- if (!existsSync23(paths.memoriesDir)) {
6336
+ if (!existsSync24(paths.memoriesDir)) {
6133
6337
  ui.error(`No .ai/memories at ${root}.`);
6134
6338
  process.exitCode = 1;
6135
6339
  return;
@@ -6141,7 +6345,7 @@ function registerMemoryRm(memory2) {
6141
6345
  process.exitCode = 1;
6142
6346
  return;
6143
6347
  }
6144
- const rel = path23.relative(root, found.filePath);
6348
+ const rel = path24.relative(root, found.filePath);
6145
6349
  if (!opts.yes) {
6146
6350
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
6147
6351
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
@@ -6165,9 +6369,9 @@ function registerMemoryRm(memory2) {
6165
6369
  }
6166
6370
 
6167
6371
  // src/commands/memory-show.ts
6168
- import { existsSync as existsSync24 } from "fs";
6372
+ import { existsSync as existsSync25 } from "fs";
6169
6373
  import { readFile as readFile11 } from "fs/promises";
6170
- import path24 from "path";
6374
+ import path25 from "path";
6171
6375
  import "commander";
6172
6376
  import {
6173
6377
  deriveConfidence,
@@ -6180,7 +6384,7 @@ function registerMemoryShow(memory2) {
6180
6384
  memory2.command("get <id>").alias("show").description("Print a memory's frontmatter, body, and confidence/usage. Mirrors MCP mem_get. Alias: show").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
6181
6385
  const root = findProjectRoot20(opts.dir);
6182
6386
  const paths = resolveHaivePaths19(root);
6183
- if (!existsSync24(paths.memoriesDir)) {
6387
+ if (!existsSync25(paths.memoriesDir)) {
6184
6388
  ui.error(`No .ai/memories at ${root}.`);
6185
6389
  process.exitCode = 1;
6186
6390
  return;
@@ -6209,7 +6413,7 @@ function registerMemoryShow(memory2) {
6209
6413
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
6210
6414
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
6211
6415
  console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
6212
- console.log(`${ui.dim("file:")} ${path24.relative(root, found.filePath)}`);
6416
+ console.log(`${ui.dim("file:")} ${path25.relative(root, found.filePath)}`);
6213
6417
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
6214
6418
  console.log(ui.dim("anchor:"));
6215
6419
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -6224,8 +6428,8 @@ function registerMemoryShow(memory2) {
6224
6428
  }
6225
6429
 
6226
6430
  // src/commands/memory-stats.ts
6227
- import { existsSync as existsSync25 } from "fs";
6228
- import path25 from "path";
6431
+ import { existsSync as existsSync26 } from "fs";
6432
+ import path26 from "path";
6229
6433
  import "commander";
6230
6434
  import {
6231
6435
  deriveConfidence as deriveConfidence2,
@@ -6238,7 +6442,7 @@ function registerMemoryStats(memory2) {
6238
6442
  memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("--hot", "only unvalidated (draft/proposed) memories read often \u2014 promotion candidates (absorbed `memory hot`)").option("--threshold <n>", "with --hot: minimum read_count to qualify", "3").option("-d, --dir <dir>", "project root").action(async (opts) => {
6239
6443
  const root = findProjectRoot21(opts.dir);
6240
6444
  const paths = resolveHaivePaths20(root);
6241
- if (!existsSync25(paths.memoriesDir)) {
6445
+ if (!existsSync26(paths.memoriesDir)) {
6242
6446
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
6243
6447
  process.exitCode = 1;
6244
6448
  return;
@@ -6269,13 +6473,13 @@ function registerMemoryStats(memory2) {
6269
6473
  console.log(
6270
6474
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
6271
6475
  );
6272
- console.log(` ${ui.dim(path25.relative(root, filePath))}`);
6476
+ console.log(` ${ui.dim(path26.relative(root, filePath))}`);
6273
6477
  }
6274
6478
  });
6275
6479
  }
6276
6480
 
6277
6481
  // src/commands/memory-impact.ts
6278
- import { existsSync as existsSync26 } from "fs";
6482
+ import { existsSync as existsSync27 } from "fs";
6279
6483
  import "commander";
6280
6484
  import {
6281
6485
  compareImpact,
@@ -6292,7 +6496,7 @@ function registerMemoryImpact(memory2) {
6292
6496
  ).option("--id <id>", "show impact for a single memory id").option("--prune", "list only prune candidates (dead weight worth reviewing)", false).option("--tier <tier>", "filter to a tier: high | medium | low | dormant").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
6293
6497
  const root = findProjectRoot22(opts.dir);
6294
6498
  const paths = resolveHaivePaths21(root);
6295
- if (!existsSync26(paths.memoriesDir)) {
6499
+ if (!existsSync27(paths.memoriesDir)) {
6296
6500
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
6297
6501
  process.exitCode = 1;
6298
6502
  return;
@@ -6363,7 +6567,7 @@ function pad(value, width) {
6363
6567
  }
6364
6568
 
6365
6569
  // src/commands/memory-feedback.ts
6366
- import { existsSync as existsSync27 } from "fs";
6570
+ import { existsSync as existsSync28 } from "fs";
6367
6571
  import { writeFile as writeFile14 } from "fs/promises";
6368
6572
  import "commander";
6369
6573
  import {
@@ -6390,7 +6594,7 @@ function registerMemoryFeedback(memory2) {
6390
6594
  }
6391
6595
  const root = findProjectRoot23(opts.dir);
6392
6596
  const paths = resolveHaivePaths22(root);
6393
- if (!existsSync27(paths.memoriesDir)) {
6597
+ if (!existsSync28(paths.memoriesDir)) {
6394
6598
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
6395
6599
  process.exitCode = 1;
6396
6600
  return;
@@ -6431,8 +6635,8 @@ function registerMemoryFeedback(memory2) {
6431
6635
 
6432
6636
  // src/commands/memory-verify.ts
6433
6637
  import { writeFile as writeFile15 } from "fs/promises";
6434
- import { existsSync as existsSync28 } from "fs";
6435
- import path26 from "path";
6638
+ import { existsSync as existsSync29 } from "fs";
6639
+ import path27 from "path";
6436
6640
  import "commander";
6437
6641
  import {
6438
6642
  findProjectRoot as findProjectRoot24,
@@ -6446,7 +6650,7 @@ function registerMemoryVerify(memory2) {
6446
6650
  ).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
6447
6651
  const root = findProjectRoot24(opts.dir);
6448
6652
  const paths = resolveHaivePaths23(root);
6449
- if (!existsSync28(paths.memoriesDir)) {
6653
+ if (!existsSync29(paths.memoriesDir)) {
6450
6654
  if (opts.json) {
6451
6655
  console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
6452
6656
  } else {
@@ -6474,7 +6678,7 @@ function registerMemoryVerify(memory2) {
6474
6678
  for (const { memory: mem, filePath } of targets) {
6475
6679
  const result = await verifyAnchor2(mem, { projectRoot: root });
6476
6680
  const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
6477
- const rel = path26.relative(root, filePath);
6681
+ const rel = path27.relative(root, filePath);
6478
6682
  if (!isAnchored) {
6479
6683
  anchorlessIds.push(mem.frontmatter.id);
6480
6684
  entries.push({ id: mem.frontmatter.id, status: "anchorless", path: rel });
@@ -6567,7 +6771,7 @@ function applyVerification(mem, result) {
6567
6771
 
6568
6772
  // src/commands/memory-import.ts
6569
6773
  import { readFile as readFile12 } from "fs/promises";
6570
- import { existsSync as existsSync29 } from "fs";
6774
+ import { existsSync as existsSync30 } from "fs";
6571
6775
  import "commander";
6572
6776
  import {
6573
6777
  findProjectRoot as findProjectRoot25,
@@ -6584,12 +6788,12 @@ function registerMemoryImport(memory2) {
6584
6788
  }
6585
6789
  const root = findProjectRoot25(opts.dir);
6586
6790
  const paths = resolveHaivePaths24(root);
6587
- if (!existsSync29(paths.haiveDir)) {
6791
+ if (!existsSync30(paths.haiveDir)) {
6588
6792
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
6589
6793
  process.exitCode = 1;
6590
6794
  return;
6591
6795
  }
6592
- if (!existsSync29(opts.from)) {
6796
+ if (!existsSync30(opts.from)) {
6593
6797
  ui.error(`File not found: ${opts.from}`);
6594
6798
  process.exitCode = 1;
6595
6799
  return;
@@ -6622,9 +6826,9 @@ function registerMemoryImport(memory2) {
6622
6826
  }
6623
6827
 
6624
6828
  // src/commands/memory-digest.ts
6625
- import { existsSync as existsSync30 } from "fs";
6829
+ import { existsSync as existsSync31 } from "fs";
6626
6830
  import { writeFile as writeFile16 } from "fs/promises";
6627
- import path27 from "path";
6831
+ import path28 from "path";
6628
6832
  import "commander";
6629
6833
  import {
6630
6834
  deriveConfidence as deriveConfidence3,
@@ -6647,7 +6851,7 @@ function registerMemoryDigest(program2) {
6647
6851
  ).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
6648
6852
  const root = findProjectRoot26(opts.dir);
6649
6853
  const paths = resolveHaivePaths25(root);
6650
- if (!existsSync30(paths.memoriesDir)) {
6854
+ if (!existsSync31(paths.memoriesDir)) {
6651
6855
  ui.error("No .ai/memories found. Run `hivelore init` first.");
6652
6856
  process.exitCode = 1;
6653
6857
  return;
@@ -6719,7 +6923,7 @@ function registerMemoryDigest(program2) {
6719
6923
  );
6720
6924
  const digest = lines.join("\n");
6721
6925
  if (opts.out) {
6722
- const outPath = path27.resolve(process.cwd(), opts.out);
6926
+ const outPath = path28.resolve(process.cwd(), opts.out);
6723
6927
  await writeFile16(outPath, digest, "utf8");
6724
6928
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
6725
6929
  } else {
@@ -6730,9 +6934,9 @@ function registerMemoryDigest(program2) {
6730
6934
 
6731
6935
  // src/commands/session-end.ts
6732
6936
  import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm } from "fs/promises";
6733
- import { existsSync as existsSync31 } from "fs";
6937
+ import { existsSync as existsSync32 } from "fs";
6734
6938
  import { spawn as spawn2 } from "child_process";
6735
- import path28 from "path";
6939
+ import path29 from "path";
6736
6940
  import { Option as Option2 } from "commander";
6737
6941
  import {
6738
6942
  buildFrontmatter as buildFrontmatter5,
@@ -6749,8 +6953,8 @@ import {
6749
6953
  writeSessionHandoff
6750
6954
  } from "@hivelore/core";
6751
6955
  async function buildAutoRecap(paths) {
6752
- const obsFile = path28.join(paths.haiveDir, ".cache", "observations.jsonl");
6753
- if (!existsSync31(obsFile)) return await buildGitAutoRecap(paths);
6956
+ const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
6957
+ if (!existsSync32(obsFile)) return await buildGitAutoRecap(paths);
6754
6958
  const raw = await readFile13(obsFile, "utf8").catch(() => "");
6755
6959
  if (!raw.trim()) return await buildGitAutoRecap(paths);
6756
6960
  const lines = raw.split("\n").filter(Boolean);
@@ -6918,8 +7122,8 @@ function runGit(cwd, args) {
6918
7122
  });
6919
7123
  }
6920
7124
  async function observationStart(paths) {
6921
- const obsFile = path28.join(paths.haiveDir, ".cache", "observations.jsonl");
6922
- if (!existsSync31(obsFile)) return null;
7125
+ const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
7126
+ if (!existsSync32(obsFile)) return null;
6923
7127
  const raw = await readFile13(obsFile, "utf8").catch(() => "");
6924
7128
  let first = null;
6925
7129
  for (const line of raw.split("\n")) {
@@ -6935,7 +7139,7 @@ async function observationStart(paths) {
6935
7139
  }
6936
7140
  async function printCaughtForYou(paths, since, quiet) {
6937
7141
  if (quiet) return;
6938
- const memories = existsSync31(paths.memoriesDir) ? await loadMemoriesFromDir10(paths.memoriesDir) : [];
7142
+ const memories = existsSync32(paths.memoriesDir) ? await loadMemoriesFromDir10(paths.memoriesDir) : [];
6939
7143
  const usage = await loadUsageIndex11(paths);
6940
7144
  const events = await loadPreventionEvents(paths);
6941
7145
  const summary = summarizeCaughtForYou(events, memories, usage, {
@@ -6973,7 +7177,7 @@ function registerSessionEnd(session2) {
6973
7177
  ).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").addOption(new Option2("--summary <text>", "alias for --accomplished").hideHelp()).option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
6974
7178
  const root = findProjectRoot27(opts.dir);
6975
7179
  const paths = resolveHaivePaths26(root);
6976
- if (!existsSync31(paths.haiveDir)) {
7180
+ if (!existsSync32(paths.haiveDir)) {
6977
7181
  if (opts.auto || opts.quiet) return;
6978
7182
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
6979
7183
  process.exitCode = 1;
@@ -7007,15 +7211,15 @@ function registerSessionEnd(session2) {
7007
7211
  });
7008
7212
  const topic = recapTopic(scope, opts.module);
7009
7213
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
7010
- const missingPaths = filesTouched.filter((p) => !existsSync31(path28.resolve(root, p)));
7214
+ const missingPaths = filesTouched.filter((p) => !existsSync32(path29.resolve(root, p)));
7011
7215
  if (missingPaths.length > 0 && !opts.quiet) {
7012
7216
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
7013
7217
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
7014
7218
  }
7015
7219
  const cleanupObservations = async () => {
7016
7220
  if (!opts.auto) return;
7017
- const obsFile = path28.join(paths.haiveDir, ".cache", "observations.jsonl");
7018
- if (existsSync31(obsFile)) await rm(obsFile).catch(() => {
7221
+ const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
7222
+ if (existsSync32(obsFile)) await rm(obsFile).catch(() => {
7019
7223
  });
7020
7224
  };
7021
7225
  const config = await loadConfig6(paths);
@@ -7039,7 +7243,7 @@ function registerSessionEnd(session2) {
7039
7243
  }
7040
7244
  return;
7041
7245
  }
7042
- if (existsSync31(paths.memoriesDir)) {
7246
+ if (existsSync32(paths.memoriesDir)) {
7043
7247
  const existing = await loadMemoriesFromDir10(paths.memoriesDir);
7044
7248
  const topicMatch = existing.find(
7045
7249
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
@@ -7060,7 +7264,7 @@ function registerSessionEnd(session2) {
7060
7264
  await cleanupObservations();
7061
7265
  if (!opts.quiet) {
7062
7266
  ui.success(`Session recap updated (revision #${revisionCount})`);
7063
- ui.info(`id=${fm.id} file=${path28.relative(root, topicMatch.filePath)}`);
7267
+ ui.info(`id=${fm.id} file=${path29.relative(root, topicMatch.filePath)}`);
7064
7268
  await printCaughtForYou(paths, caughtSince, opts.quiet);
7065
7269
  ui.info("Tip: `hivelore stats --export-report` generates a usage JSON suitable for dashboards.");
7066
7270
  }
@@ -7078,12 +7282,12 @@ function registerSessionEnd(session2) {
7078
7282
  status: "validated"
7079
7283
  });
7080
7284
  const file = memoryFilePath5(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
7081
- await mkdir11(path28.dirname(file), { recursive: true });
7285
+ await mkdir11(path29.dirname(file), { recursive: true });
7082
7286
  await writeFile17(file, serializeMemory12({ frontmatter, body }), "utf8");
7083
7287
  await cleanupObservations();
7084
7288
  if (!opts.quiet) {
7085
7289
  ui.success(`Session recap created`);
7086
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path28.relative(root, file)}`);
7290
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path29.relative(root, file)}`);
7087
7291
  await printCaughtForYou(paths, caughtSince, opts.quiet);
7088
7292
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
7089
7293
  ui.info("Tip: export a local MCP usage rollup with `hivelore stats --export-report .ai/tool-usage-roi-report.json`.");
@@ -7096,29 +7300,47 @@ function parseCsv5(value) {
7096
7300
  }
7097
7301
  function normalizeAnchorPath(root, filePath) {
7098
7302
  if (!filePath) return filePath;
7099
- if (!path28.isAbsolute(filePath)) return filePath;
7100
- const rel = path28.relative(root, filePath);
7303
+ if (!path29.isAbsolute(filePath)) return filePath;
7304
+ const rel = path29.relative(root, filePath);
7101
7305
  if (rel.startsWith("..")) return filePath;
7102
7306
  return rel;
7103
7307
  }
7104
7308
 
7105
7309
  // src/commands/stats.ts
7106
7310
  import "commander";
7107
- import { existsSync as existsSync32 } from "fs";
7311
+ import { existsSync as existsSync33 } from "fs";
7108
7312
  import { mkdir as mkdir12, writeFile as writeFile18 } from "fs/promises";
7109
- import path29 from "path";
7313
+ import path30 from "path";
7110
7314
  import {
7111
7315
  aggregateUsage,
7316
+ buildPreventionReceipt,
7317
+ renderPreventionReceipt,
7112
7318
  findProjectRoot as findProjectRoot28,
7113
7319
  loadMemoriesFromDir as loadMemoriesFromDir11,
7114
7320
  loadUsageIndex as loadUsageIndex12,
7321
+ loadPreventionEvents as loadPreventionEvents2,
7115
7322
  parseSince,
7116
7323
  readUsageEvents,
7117
7324
  resolveHaivePaths as resolveHaivePaths27,
7118
7325
  usageLogSize
7119
7326
  } from "@hivelore/core";
7120
7327
  function registerStats(program2) {
7121
- program2.command("stats").description("Show MCP tool-usage stats over a window (e.g. --since 7d).").option("--since <window>", "ISO date or relative (e.g. '7d', '24h', '30m')", "30d").option("--json", "emit JSON instead of human-readable output", false).option("--memory-hits", "show top-read memories (which mems are actually being used)", false).option(
7328
+ const stats = program2.command("stats").description("Show MCP tool-usage stats and prevention receipts.");
7329
+ stats.command("receipt").description("Show documented mistakes refused by the gate over a time window").addHelpText("after", "\nParent options also apply: --since <window> (default 7d here), --json, --dir <dir>.").action(async () => {
7330
+ const opts = stats.opts();
7331
+ const root = findProjectRoot28(opts.dir);
7332
+ const paths = resolveHaivePaths27(root);
7333
+ const sinceRaw = stats.getOptionValueSource("since") === "default" ? "7d" : opts.since ?? "7d";
7334
+ const since = parseSince(sinceRaw) ?? new Date(Date.now() - 7 * 864e5);
7335
+ const [events, usage, memories] = await Promise.all([
7336
+ loadPreventionEvents2(paths),
7337
+ loadUsageIndex12(paths),
7338
+ existsSync33(paths.memoriesDir) ? loadMemoriesFromDir11(paths.memoriesDir) : Promise.resolve([])
7339
+ ]);
7340
+ const receipt = buildPreventionReceipt(events, memories, usage, { since });
7341
+ console.log(opts.json ? JSON.stringify(receipt, null, 2) : renderPreventionReceipt(receipt));
7342
+ });
7343
+ stats.option("--since <window>", "ISO date or relative (e.g. '7d', '24h', '30m')", "30d").option("--json", "emit JSON instead of human-readable output", false).option("--memory-hits", "show top-read memories (which mems are actually being used)", false).option(
7122
7344
  "--export-report <path>",
7123
7345
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
7124
7346
  void 0
@@ -7178,11 +7400,11 @@ function registerStats(program2) {
7178
7400
  });
7179
7401
  }
7180
7402
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
7181
- const outAbs = path29.isAbsolute(outRelative) ? path29.resolve(outRelative) : path29.resolve(root, outRelative);
7403
+ const outAbs = path30.isAbsolute(outRelative) ? path30.resolve(outRelative) : path30.resolve(root, outRelative);
7182
7404
  const size = await usageLogSize(paths);
7183
7405
  let events = await readUsageEvents(paths);
7184
7406
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
7185
- if (existsSync32(paths.memoriesDir)) {
7407
+ if (existsSync33(paths.memoriesDir)) {
7186
7408
  const mems = await loadMemoriesFromDir11(paths.memoriesDir);
7187
7409
  for (const { memory: memory2 } of mems) {
7188
7410
  const fm = memory2.frontmatter;
@@ -7212,7 +7434,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
7212
7434
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
7213
7435
  events = [];
7214
7436
  }
7215
- await mkdir12(path29.dirname(outAbs), { recursive: true });
7437
+ await mkdir12(path30.dirname(outAbs), { recursive: true });
7216
7438
  const payload = {
7217
7439
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
7218
7440
  project_root: root,
@@ -7401,9 +7623,9 @@ function summarize(name, t0, payload, notes) {
7401
7623
  }
7402
7624
 
7403
7625
  // src/commands/benchmark.ts
7404
- import { existsSync as existsSync33 } from "fs";
7626
+ import { existsSync as existsSync34 } from "fs";
7405
7627
  import { readdir as readdir3, readFile as readFile14, writeFile as writeFile19 } from "fs/promises";
7406
- import path30 from "path";
7628
+ import path31 from "path";
7407
7629
  import "commander";
7408
7630
  import { estimateTokens as estimateTokens2, findProjectRoot as findProjectRoot30 } from "@hivelore/core";
7409
7631
  function registerBenchmark(program2) {
@@ -7418,9 +7640,9 @@ function registerBenchmark(program2) {
7418
7640
  }
7419
7641
  const markdown = renderMarkdown(root, summary, rows);
7420
7642
  if (opts.out) {
7421
- const outFile = path30.isAbsolute(opts.out) ? opts.out : path30.join(root, opts.out);
7643
+ const outFile = path31.isAbsolute(opts.out) ? opts.out : path31.join(root, opts.out);
7422
7644
  await writeFile19(outFile, markdown, "utf8");
7423
- ui.success(`wrote ${path30.relative(process.cwd(), outFile)}`);
7645
+ ui.success(`wrote ${path31.relative(process.cwd(), outFile)}`);
7424
7646
  return;
7425
7647
  }
7426
7648
  console.log(markdown);
@@ -7444,19 +7666,19 @@ function registerBenchmark(program2) {
7444
7666
  }
7445
7667
  function resolveBenchmarkRoot(dir) {
7446
7668
  const candidate = dir ?? "benchmarks/agent-benchmark";
7447
- if (path30.isAbsolute(candidate)) return candidate;
7669
+ if (path31.isAbsolute(candidate)) return candidate;
7448
7670
  const projectRoot = findProjectRoot30(process.cwd());
7449
- return path30.join(projectRoot, candidate);
7671
+ return path31.join(projectRoot, candidate);
7450
7672
  }
7451
7673
  async function collectRows(root) {
7452
- if (!existsSync33(root)) throw new Error(`Benchmark directory not found: ${root}`);
7674
+ if (!existsSync34(root)) throw new Error(`Benchmark directory not found: ${root}`);
7453
7675
  const entries = await readdir3(root, { withFileTypes: true });
7454
7676
  const rows = [];
7455
7677
  for (const entry of entries) {
7456
7678
  if (!entry.isDirectory()) continue;
7457
- const fixtureDir = path30.join(root, entry.name);
7458
- const reportFile = path30.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
7459
- if (!existsSync33(reportFile)) continue;
7679
+ const fixtureDir = path31.join(root, entry.name);
7680
+ const reportFile = path31.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
7681
+ if (!existsSync34(reportFile)) continue;
7460
7682
  const report = await readFile14(reportFile, "utf8");
7461
7683
  rows.push(parseAgentReport(entry.name, report));
7462
7684
  }
@@ -7549,8 +7771,8 @@ function escapeRegExp(value) {
7549
7771
 
7550
7772
  // src/commands/eval.ts
7551
7773
  import { mkdir as mkdir13, readFile as readFile15, writeFile as writeFile20 } from "fs/promises";
7552
- import { existsSync as existsSync34 } from "fs";
7553
- import path31 from "path";
7774
+ import { existsSync as existsSync35 } from "fs";
7775
+ import path32 from "path";
7554
7776
  import "commander";
7555
7777
  import {
7556
7778
  aggregateRetrieval,
@@ -7564,7 +7786,7 @@ import {
7564
7786
  findProjectRoot as findProjectRoot31,
7565
7787
  loadConfig as loadConfig7,
7566
7788
  loadEvalHistory,
7567
- loadPreventionEvents as loadPreventionEvents2,
7789
+ loadPreventionEvents as loadPreventionEvents3,
7568
7790
  loadUsageIndex as loadUsageIndex13,
7569
7791
  overallScore,
7570
7792
  resolveHaivePaths as resolveHaivePaths29,
@@ -7578,7 +7800,7 @@ function registerEval(program2) {
7578
7800
  ).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--fail-under-catch-rate <pct>", "exit non-zero if sensor catch-rate is below this percentage").option("--fail-under-gate-precision <pct>", "exit non-zero if gate precision is below this percentage").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("--regression-gate", "CI-safe gate: compare against the baseline IF one exists (fail on regression), else no-op", false).option("--record", "append this run's score to .ai/.cache/eval-history.jsonl (trend the harness over time)", false).option("--trend", "print the recorded score trend (sparkline + latest/best/delta) and exit", false).option("--ref <ref>", "version/commit label stored with a --record run").option("-d, --dir <dir>", "project root").action(async (opts) => {
7579
7801
  const root = findProjectRoot31(opts.dir);
7580
7802
  const paths = resolveHaivePaths29(root);
7581
- if (!existsSync34(paths.memoriesDir)) {
7803
+ if (!existsSync35(paths.memoriesDir)) {
7582
7804
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
7583
7805
  process.exitCode = 1;
7584
7806
  return;
@@ -7631,7 +7853,7 @@ function registerEval(program2) {
7631
7853
  const authoredScore = resolvedSpec.authored > 0 && resolvedSpec.synthesized > 0 && (authoredRetrievalAgg || sensorAgg) ? overallScore(authoredRetrievalAgg, sensorAgg) : null;
7632
7854
  const [usage, preventionEvents, config] = await Promise.all([
7633
7855
  loadUsageIndex13(paths),
7634
- loadPreventionEvents2(paths),
7856
+ loadPreventionEvents3(paths),
7635
7857
  loadConfig7(paths)
7636
7858
  ]);
7637
7859
  const gatePrecision = computeGatePrecision(
@@ -7650,7 +7872,7 @@ function registerEval(program2) {
7650
7872
  });
7651
7873
  if (!opts.json) ui.success(`Recorded eval score ${report.score}/100 to history.`);
7652
7874
  }
7653
- const baselineFile = opts.baselineFile ? path31.isAbsolute(opts.baselineFile) ? opts.baselineFile : path31.join(root, opts.baselineFile) : path31.join(root, ".ai", "eval", "baseline.json");
7875
+ const baselineFile = opts.baselineFile ? path32.isAbsolute(opts.baselineFile) ? opts.baselineFile : path32.join(root, opts.baselineFile) : path32.join(root, ".ai", "eval", "baseline.json");
7654
7876
  if (opts.baseline) {
7655
7877
  const snapshot = {
7656
7878
  saved_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -7659,18 +7881,18 @@ function registerEval(program2) {
7659
7881
  report,
7660
7882
  gate_precision: gatePrecision
7661
7883
  };
7662
- await mkdir13(path31.dirname(baselineFile), { recursive: true });
7884
+ await mkdir13(path32.dirname(baselineFile), { recursive: true });
7663
7885
  await writeFile20(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
7664
- if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path31.relative(root, baselineFile)}`);
7886
+ if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path32.relative(root, baselineFile)}`);
7665
7887
  }
7666
7888
  let delta = null;
7667
7889
  let gateDelta = null;
7668
7890
  if (opts.compare || opts.regressionGate) {
7669
- if (!existsSync34(baselineFile)) {
7891
+ if (!existsSync35(baselineFile)) {
7670
7892
  if (opts.regressionGate) {
7671
- if (!opts.json) ui.info(`No baseline at ${path31.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`hivelore eval --baseline\` to enable it.`);
7893
+ if (!opts.json) ui.info(`No baseline at ${path32.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`hivelore eval --baseline\` to enable it.`);
7672
7894
  } else {
7673
- ui.error(`No baseline at ${path31.relative(root, baselineFile)}. Run \`hivelore eval --baseline\` first.`);
7895
+ ui.error(`No baseline at ${path32.relative(root, baselineFile)}. Run \`hivelore eval --baseline\` first.`);
7674
7896
  process.exitCode = 1;
7675
7897
  return;
7676
7898
  }
@@ -7713,9 +7935,9 @@ function registerEval(program2) {
7713
7935
  }
7714
7936
  const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision, authoredScore);
7715
7937
  if (opts.out) {
7716
- const outFile = path31.isAbsolute(opts.out) ? opts.out : path31.join(root, opts.out);
7938
+ const outFile = path32.isAbsolute(opts.out) ? opts.out : path32.join(root, opts.out);
7717
7939
  await writeFile20(outFile, md, "utf8");
7718
- ui.success(`wrote ${path31.relative(process.cwd(), outFile)}`);
7940
+ ui.success(`wrote ${path32.relative(process.cwd(), outFile)}`);
7719
7941
  } else {
7720
7942
  console.log(md);
7721
7943
  }
@@ -7799,13 +8021,13 @@ function countCases(spec) {
7799
8021
  }
7800
8022
  async function resolveSpec(opts, root, memoriesDir) {
7801
8023
  if (opts.spec) {
7802
- const file = path31.resolve(opts.spec);
8024
+ const file = path32.resolve(opts.spec);
7803
8025
  const raw = await readFile15(file, "utf8");
7804
8026
  const spec = JSON.parse(raw);
7805
8027
  return { spec, source: file, synthesized: 0, authored: countCases(spec) };
7806
8028
  }
7807
- const defaultSpec = path31.join(root, ".ai", "eval", "spec.json");
7808
- if (existsSync34(defaultSpec)) {
8029
+ const defaultSpec = path32.join(root, ".ai", "eval", "spec.json");
8030
+ if (existsSync35(defaultSpec)) {
7809
8031
  const raw = await readFile15(defaultSpec, "utf8");
7810
8032
  const explicit = JSON.parse(raw);
7811
8033
  const memories2 = await loadMemoriesFromDir(memoriesDir);
@@ -7932,8 +8154,8 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision, authoredScore
7932
8154
 
7933
8155
  // src/commands/memory-suggest.ts
7934
8156
  import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
7935
- import { existsSync as existsSync35 } from "fs";
7936
- import path32 from "path";
8157
+ import { existsSync as existsSync36 } from "fs";
8158
+ import path33 from "path";
7937
8159
  import "commander";
7938
8160
  import {
7939
8161
  MemoryTypeSchema,
@@ -8023,7 +8245,7 @@ function registerMemorySuggest(memory2) {
8023
8245
  }
8024
8246
  const created = [];
8025
8247
  const skipped = [];
8026
- const existing = existsSync35(paths.memoriesDir) ? await loadMemoriesFromDir12(paths.memoriesDir) : [];
8248
+ const existing = existsSync36(paths.memoriesDir) ? await loadMemoriesFromDir12(paths.memoriesDir) : [];
8027
8249
  for (const s of top) {
8028
8250
  const slug = slugify2(s.query);
8029
8251
  if (!slug) {
@@ -8046,13 +8268,13 @@ function registerMemorySuggest(memory2) {
8046
8268
  });
8047
8269
  const body = renderTemplate(s, fm.id, status);
8048
8270
  const file = memoryFilePath6(paths, fm.scope, fm.id, fm.module);
8049
- await mkdir14(path32.dirname(file), { recursive: true });
8050
- if (existsSync35(file)) {
8051
- skipped.push({ query: s.query, reason: `file already exists at ${path32.relative(root, file)}` });
8271
+ await mkdir14(path33.dirname(file), { recursive: true });
8272
+ if (existsSync36(file)) {
8273
+ skipped.push({ query: s.query, reason: `file already exists at ${path33.relative(root, file)}` });
8052
8274
  continue;
8053
8275
  }
8054
8276
  await writeFile21(file, serializeMemory13({ frontmatter: fm, body }), "utf8");
8055
- created.push({ id: fm.id, file: path32.relative(root, file), query: s.query });
8277
+ created.push({ id: fm.id, file: path33.relative(root, file), query: s.query });
8056
8278
  }
8057
8279
  if (opts.json) {
8058
8280
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -8150,8 +8372,8 @@ function truncate2(text, max) {
8150
8372
  }
8151
8373
 
8152
8374
  // src/commands/memory-conflict-candidates.ts
8153
- import { existsSync as existsSync36 } from "fs";
8154
- import path33 from "path";
8375
+ import { existsSync as existsSync37 } from "fs";
8376
+ import path34 from "path";
8155
8377
  import "commander";
8156
8378
  import {
8157
8379
  findLexicalConflictPairs,
@@ -8187,9 +8409,9 @@ function registerMemoryConflictCandidates(memory2) {
8187
8409
  });
8188
8410
  }
8189
8411
  async function runConflictCandidates(opts) {
8190
- const root = path33.resolve(opts.dir ?? process.cwd());
8412
+ const root = path34.resolve(opts.dir ?? process.cwd());
8191
8413
  const paths = resolveHaivePaths31(findProjectRoot33(root));
8192
- if (!existsSync36(paths.memoriesDir)) {
8414
+ if (!existsSync37(paths.memoriesDir)) {
8193
8415
  ui.error("No memories \u2014 run `hivelore init`.");
8194
8416
  process.exitCode = 1;
8195
8417
  return;
@@ -8223,9 +8445,9 @@ async function runConflictCandidates(opts) {
8223
8445
  }
8224
8446
 
8225
8447
  // src/commands/memory-archive.ts
8226
- import { existsSync as existsSync37 } from "fs";
8448
+ import { existsSync as existsSync38 } from "fs";
8227
8449
  import { writeFile as writeFile22 } from "fs/promises";
8228
- import path34 from "path";
8450
+ import path35 from "path";
8229
8451
  import "commander";
8230
8452
  import {
8231
8453
  findProjectRoot as findProjectRoot34,
@@ -8244,7 +8466,7 @@ function registerMemoryArchive(memory2) {
8244
8466
  ).option("--since <window>", "minimum age since last read (e.g. '180d', '6m'). Default: enforcement.decayAfterDays or 180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--unread", "decay by unread-age ALONE (ignore anchor status) \u2014 more aggressive corpus hygiene", false).option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
8245
8467
  const root = findProjectRoot34(opts.dir);
8246
8468
  const paths = resolveHaivePaths32(root);
8247
- if (!existsSync37(paths.memoriesDir)) {
8469
+ if (!existsSync38(paths.memoriesDir)) {
8248
8470
  ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
8249
8471
  process.exitCode = 1;
8250
8472
  return;
@@ -8269,7 +8491,7 @@ function registerMemoryArchive(memory2) {
8269
8491
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
8270
8492
  const retired = retirementSignal2(fm, mem.body);
8271
8493
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
8272
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync37(path34.join(paths.root, p)));
8494
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync38(path35.join(paths.root, p)));
8273
8495
  const isAnchorless = !hasAnyAnchor;
8274
8496
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
8275
8497
  const u = getUsage8(usage, fm.id);
@@ -8344,13 +8566,14 @@ function parseDays(input) {
8344
8566
  }
8345
8567
 
8346
8568
  // src/commands/doctor.ts
8347
- import { existsSync as existsSync38, statSync as statSync2 } from "fs";
8569
+ import { existsSync as existsSync39, statSync as statSync2 } from "fs";
8348
8570
  import { readFile as readFile16, stat, writeFile as writeFile23 } from "fs/promises";
8349
- import path35 from "path";
8571
+ import path36 from "path";
8350
8572
  import { execFileSync, execSync } from "child_process";
8351
8573
  import "commander";
8352
8574
  import {
8353
8575
  codeMapPath as codeMapPath2,
8576
+ assessSensorHealth as assessSensorHealth2,
8354
8577
  countSourceFilesOnDisk,
8355
8578
  extractReferencedPaths,
8356
8579
  sensorPatternBrittleness,
@@ -8360,7 +8583,8 @@ import {
8360
8583
  isStackPackSeed as isStackPackSeed2,
8361
8584
  loadCodeMap as loadCodeMap6,
8362
8585
  loadConfig as loadConfig10,
8363
- loadMemoriesFromDir as loadMemoriesFromDir14,
8586
+ loadMemoriesFromDirDetailed,
8587
+ loadSensorLedger as loadSensorLedger2,
8364
8588
  loadUsageIndex as loadUsageIndex15,
8365
8589
  readUsageEvents as readUsageEvents3,
8366
8590
  resolveHaivePaths as resolveHaivePaths33
@@ -8375,7 +8599,7 @@ function registerDoctor(program2) {
8375
8599
  const findings = [];
8376
8600
  const repairs = [];
8377
8601
  const config = await loadConfig10(paths);
8378
- if (!existsSync38(paths.haiveDir)) {
8602
+ if (!existsSync39(paths.haiveDir)) {
8379
8603
  if (opts.json) {
8380
8604
  console.log(JSON.stringify({
8381
8605
  initialized: false,
@@ -8400,7 +8624,7 @@ function registerDoctor(program2) {
8400
8624
  })
8401
8625
  );
8402
8626
  }
8403
- if (!existsSync38(paths.projectContext)) {
8627
+ if (!existsSync39(paths.projectContext)) {
8404
8628
  findings.push({
8405
8629
  severity: "warn",
8406
8630
  code: "no-project-context",
@@ -8420,7 +8644,7 @@ function registerDoctor(program2) {
8420
8644
  });
8421
8645
  } else {
8422
8646
  const referenced = extractReferencedPaths(content);
8423
- const missing = referenced.filter((p) => !existsSync38(path35.resolve(root, p)));
8647
+ const missing = referenced.filter((p) => !existsSync39(path36.resolve(root, p)));
8424
8648
  const grounded = referenced.length - missing.length;
8425
8649
  if (referenced.length >= 3 && grounded / referenced.length < 0.5) {
8426
8650
  findings.push({
@@ -8442,13 +8666,23 @@ function registerDoctor(program2) {
8442
8666
  });
8443
8667
  }
8444
8668
  }
8445
- const memories = existsSync38(paths.memoriesDir) ? await loadMemoriesFromDir14(paths.memoriesDir) : [];
8669
+ const memoriesDetailed = existsSync39(paths.memoriesDir) ? await loadMemoriesFromDirDetailed(paths.memoriesDir) : { loaded: [], invalid: [] };
8670
+ const memories = memoriesDetailed.loaded;
8446
8671
  const now = Date.now();
8672
+ if (memoriesDetailed.invalid.length > 0) {
8673
+ const listed = memoriesDetailed.invalid.slice(0, 5).map((f) => `${path36.relative(root, f.filePath)} (${f.error})`).join("; ");
8674
+ findings.push({
8675
+ severity: "warn",
8676
+ code: "invalid-memory-files",
8677
+ message: `${memoriesDetailed.invalid.length} memory file(s) failed to parse and are INVISIBLE to briefings and the gate: ${listed}`,
8678
+ fix: "Fix the frontmatter (or delete the file) \u2014 a corrupt memory is a silently lost team lesson."
8679
+ });
8680
+ }
8447
8681
  if (memories.length === 0) {
8448
8682
  findings.push({
8449
8683
  severity: "info",
8450
8684
  code: "no-memories",
8451
- message: "No memories yet. Capture knowledge as agents work via mem_save / mem_observe / mem_tried."
8685
+ message: "No memories yet. Capture knowledge as agents work via mem_save / mem_tried."
8452
8686
  });
8453
8687
  } else {
8454
8688
  const usage = await loadUsageIndex15(paths);
@@ -8458,7 +8692,7 @@ function registerDoctor(program2) {
8458
8692
  severity: "warn",
8459
8693
  code: "stale-memories",
8460
8694
  message: `${stale.length} memor${stale.length === 1 ? "y" : "ies"} marked stale (anchored code drifted).`,
8461
- fix: "hivelore memory verify --update # re-check anchors\nhaive memory edit <id> # manually refresh body"
8695
+ fix: "hivelore memory verify --update # re-check anchors\nhivelore memory update <id> # manually refresh body"
8462
8696
  });
8463
8697
  }
8464
8698
  const proposed = memories.filter((m) => m.memory.frontmatter.status === "proposed");
@@ -8546,6 +8780,18 @@ function registerDoctor(program2) {
8546
8780
  });
8547
8781
  }
8548
8782
  const sensorMemories = memories.filter((m) => m.memory.frontmatter.sensor);
8783
+ const gateMissDrafts = memories.filter(
8784
+ (m) => m.memory.frontmatter.status === "proposed" && m.memory.frontmatter.tags.includes("gate-miss")
8785
+ );
8786
+ if (gateMissDrafts.length > 0) {
8787
+ findings.push({
8788
+ severity: "info",
8789
+ code: "gate-miss-drafts",
8790
+ section: "Corpus health",
8791
+ message: `${gateMissDrafts.length} proposed gate-miss lesson(s) await review: ${gateMissDrafts.slice(0, 5).map((m) => m.memory.frontmatter.id).join(", ")}.`,
8792
+ fix: "Review with `hivelore memory list --status proposed`."
8793
+ });
8794
+ }
8549
8795
  const blockSensors = sensorMemories.filter(
8550
8796
  (m) => m.memory.frontmatter.sensor?.severity === "block"
8551
8797
  ).length;
@@ -8565,8 +8811,8 @@ function registerDoctor(program2) {
8565
8811
  const anchorPaths = s.paths.length > 0 ? s.paths : m.memory.frontmatter.anchor.paths;
8566
8812
  const targets = [];
8567
8813
  for (const rel of anchorPaths) {
8568
- const abs = path35.resolve(root, rel);
8569
- if (!existsSync38(abs)) continue;
8814
+ const abs = path36.resolve(root, rel);
8815
+ if (!existsSync39(abs)) continue;
8570
8816
  try {
8571
8817
  targets.push({ path: rel, content: await readFile16(abs, "utf8") });
8572
8818
  } catch {
@@ -8586,6 +8832,26 @@ function registerDoctor(program2) {
8586
8832
  fix: "Make the pattern discriminating (add an 'absent' companion), or demote: `hivelore sensors promote <id> --severity warn`"
8587
8833
  });
8588
8834
  }
8835
+ const sensorHealth = assessSensorHealth2(await loadSensorLedger2(paths));
8836
+ for (const health of sensorHealth.filter((h) => h.quarantine_pending)) {
8837
+ const last = health.flaps.at(-1);
8838
+ findings.push({
8839
+ severity: "warn",
8840
+ code: "sensor-flaky",
8841
+ section: "Protection",
8842
+ message: `${health.memory_id} flapped ${health.flap_count}\xD7 on identical inputs. Last contradiction: ${last.previous.at} ${last.previous.outcome} \u2192 ${last.current.at} ${last.current.outcome}.`,
8843
+ fix: "Run `hivelore sync` to demote it, fix the oracle, then `hivelore sensors promote <id> --yes`."
8844
+ });
8845
+ }
8846
+ for (const health of sensorHealth.filter((h) => h.never_fired)) {
8847
+ findings.push({
8848
+ severity: "info",
8849
+ code: "sensor-never-fired",
8850
+ section: "Protection",
8851
+ message: `${health.memory_id} was evaluated ${health.evaluation_count} times across 30+ days and never fired \u2014 retirement candidate.`,
8852
+ fix: "Review the sensor manually; no automatic delete/retire command is provided."
8853
+ });
8854
+ }
8589
8855
  const codeMap = await loadCodeMap6(paths);
8590
8856
  if (!codeMap) {
8591
8857
  findings.push({
@@ -8655,9 +8921,9 @@ function registerDoctor(program2) {
8655
8921
  }
8656
8922
  }
8657
8923
  if (config.enforcement?.requireBriefingFirst) {
8658
- const claudeSettings = path35.join(root, ".claude", "settings.local.json");
8924
+ const claudeSettings = path36.join(root, ".claude", "settings.local.json");
8659
8925
  let hasClaudeEnforcement = false;
8660
- if (existsSync38(claudeSettings)) {
8926
+ if (existsSync39(claudeSettings)) {
8661
8927
  try {
8662
8928
  const { readFile: readFile24 } = await import("fs/promises");
8663
8929
  const raw = await readFile24(claudeSettings, "utf8");
@@ -8683,7 +8949,7 @@ function registerDoctor(program2) {
8683
8949
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
8684
8950
  });
8685
8951
  }
8686
- findings.push(...await collectInstallFindings(root, "0.33.0"));
8952
+ findings.push(...await collectInstallFindings(root, "0.35.0"));
8687
8953
  findings.push(...await collectToolchainFindings(root));
8688
8954
  try {
8689
8955
  const legacyRaw = execSync("haive-mcp --version", {
@@ -8691,7 +8957,7 @@ function registerDoctor(program2) {
8691
8957
  timeout: 3e3,
8692
8958
  stdio: ["ignore", "pipe", "ignore"]
8693
8959
  }).trim();
8694
- const cliVersion = "0.33.0";
8960
+ const cliVersion = "0.35.0";
8695
8961
  if (legacyRaw && legacyRaw !== cliVersion) {
8696
8962
  findings.push({
8697
8963
  severity: "warn",
@@ -8707,17 +8973,17 @@ npm uninstall -g @hivelore/mcp`
8707
8973
  }
8708
8974
  {
8709
8975
  const configPaths = [
8710
- path35.join(root, ".mcp.json"),
8711
- path35.join(root, ".cursor", "mcp.json"),
8712
- path35.join(root, ".vscode", "mcp.json")
8976
+ path36.join(root, ".mcp.json"),
8977
+ path36.join(root, ".cursor", "mcp.json"),
8978
+ path36.join(root, ".vscode", "mcp.json")
8713
8979
  ];
8714
8980
  const staleConfigs = [];
8715
8981
  for (const cfgPath of configPaths) {
8716
- if (!existsSync38(cfgPath)) continue;
8982
+ if (!existsSync39(cfgPath)) continue;
8717
8983
  try {
8718
8984
  const raw = await readFile16(cfgPath, "utf8");
8719
8985
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
8720
- staleConfigs.push(path35.relative(root, cfgPath));
8986
+ staleConfigs.push(path36.relative(root, cfgPath));
8721
8987
  if (opts.fix && !opts.dryRun) {
8722
8988
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "hivelore"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
8723
8989
  await writeFile23(cfgPath, updated, "utf8");
@@ -9010,8 +9276,8 @@ which -a hivelore haive`
9010
9276
  const missingBins = /* @__PURE__ */ new Map();
9011
9277
  const staleBins = /* @__PURE__ */ new Map();
9012
9278
  for (const rel of integrationFiles) {
9013
- const file = path35.join(root, rel);
9014
- if (!existsSync38(file)) continue;
9279
+ const file = path36.join(root, rel);
9280
+ if (!existsSync39(file)) continue;
9015
9281
  const text = await readFile16(file, "utf8").catch(() => "");
9016
9282
  for (const bin of extractAbsoluteHaiveBins(text)) {
9017
9283
  const version = versionForBinary(bin);
@@ -9044,7 +9310,7 @@ which -a hivelore haive`
9044
9310
  async function collectToolchainFindings(root) {
9045
9311
  const findings = [];
9046
9312
  const pkg = await readJson(
9047
- path35.join(root, "package.json")
9313
+ path36.join(root, "package.json")
9048
9314
  );
9049
9315
  const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
9050
9316
  if (!wantsPnpm) return findings;
@@ -9063,10 +9329,10 @@ async function collectToolchainFindings(root) {
9063
9329
  }
9064
9330
  async function collectDistFreshnessFindings(root, expectedVersion) {
9065
9331
  const findings = [];
9066
- const isHaiveWorkspace = ["hivelore-monorepo", "haive-monorepo"].includes((await readJson(path35.join(root, "package.json")))?.name ?? "");
9332
+ const isHaiveWorkspace = ["hivelore-monorepo", "haive-monorepo"].includes((await readJson(path36.join(root, "package.json")))?.name ?? "");
9067
9333
  if (!isHaiveWorkspace) return findings;
9068
- const cliDist = path35.join(root, "packages/cli/dist/index.js");
9069
- if (!existsSync38(cliDist)) {
9334
+ const cliDist = path36.join(root, "packages/cli/dist/index.js");
9335
+ if (!existsSync39(cliDist)) {
9070
9336
  findings.push({
9071
9337
  severity: "warn",
9072
9338
  code: "workspace-dist-missing",
@@ -9090,7 +9356,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
9090
9356
  "packages/core/src/index.ts",
9091
9357
  "packages/mcp/src/server.ts",
9092
9358
  "packages/cli/src/index.ts"
9093
- ].map((rel) => path35.join(root, rel)).filter(existsSync38);
9359
+ ].map((rel) => path36.join(root, rel)).filter(existsSync39);
9094
9360
  if (sourceFiles.length > 0) {
9095
9361
  const distMtime = statSync2(cliDist).mtimeMs;
9096
9362
  const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
@@ -9108,7 +9374,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
9108
9374
  }
9109
9375
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
9110
9376
  const findings = [];
9111
- const rootPkg = await readJson(path35.join(root, "package.json"));
9377
+ const rootPkg = await readJson(path36.join(root, "package.json"));
9112
9378
  const workspacePackages = [
9113
9379
  "packages/core/package.json",
9114
9380
  "packages/embeddings/package.json",
@@ -9117,7 +9383,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
9117
9383
  ];
9118
9384
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
9119
9385
  rel,
9120
- pkg: await readJson(path35.join(root, rel))
9386
+ pkg: await readJson(path36.join(root, rel))
9121
9387
  })))).filter((item) => item.pkg);
9122
9388
  const isHaiveWorkspace = rootPkg?.name === "hivelore-monorepo" || rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hivelore/"));
9123
9389
  if (!isHaiveWorkspace) return findings;
@@ -9179,7 +9445,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
9179
9445
  }
9180
9446
  }
9181
9447
  async function readJson(file) {
9182
- if (!existsSync38(file)) return null;
9448
+ if (!existsSync39(file)) return null;
9183
9449
  try {
9184
9450
  return JSON.parse(await readFile16(file, "utf8"));
9185
9451
  } catch {
@@ -9408,27 +9674,29 @@ function runCommand(cmd, args, cwd) {
9408
9674
  }
9409
9675
 
9410
9676
  // src/commands/resolve-project.ts
9411
- import path36 from "path";
9677
+ import path37 from "path";
9412
9678
  import "commander";
9413
9679
  import { resolveProjectInfo } from "@hivelore/core";
9414
9680
  function registerResolveProject(program2) {
9415
9681
  program2.command("resolve-project").description(
9416
9682
  "Print JSON for Hivelore project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
9417
9683
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
9418
- const info = resolveProjectInfo({ cwd: path36.resolve(opts.dir) });
9684
+ const info = resolveProjectInfo({ cwd: path37.resolve(opts.dir) });
9419
9685
  console.log(JSON.stringify({ ok: true, info }, null, 2));
9420
9686
  });
9421
9687
  }
9422
9688
 
9423
9689
  // src/commands/enforce.ts
9424
- import { execFile as execFile4, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
9425
- import { existsSync as existsSync40, statSync as statSync3 } from "fs";
9690
+ import { execFile as execFile5, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
9691
+ import { existsSync as existsSync41, statSync as statSync3 } from "fs";
9426
9692
  import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as rm2, writeFile as writeFile25 } from "fs/promises";
9427
- import path38 from "path";
9428
- import { promisify as promisify4 } from "util";
9693
+ import path39 from "path";
9694
+ import { promisify as promisify5 } from "util";
9429
9695
  import "commander";
9430
9696
  import {
9431
9697
  antiPatternGateParams as antiPatternGateParams2,
9698
+ appendSensorEvaluations,
9699
+ assessSensorHealth as assessSensorHealth3,
9432
9700
  assessBootstrapState,
9433
9701
  isSensorScannablePath,
9434
9702
  findProjectRoot as findProjectRoot37,
@@ -9441,7 +9709,8 @@ import {
9441
9709
  isRetiredMemory as isRetiredMemory2,
9442
9710
  loadConfig as loadConfig12,
9443
9711
  detectAgentContext,
9444
- loadMemoriesFromDir as loadMemoriesFromDir15,
9712
+ loadMemoriesFromDir as loadMemoriesFromDir14,
9713
+ loadSensorLedger as loadSensorLedger3,
9445
9714
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
9446
9715
  readRecentBriefingMarker,
9447
9716
  recordPreventionHits,
@@ -9451,15 +9720,16 @@ import {
9451
9720
  saveConfig as saveConfig3,
9452
9721
  selectCommandSensors,
9453
9722
  sensorTargetsFromDiff,
9723
+ sensorAppliesToPath as sensorAppliesToPath2,
9454
9724
  SESSION_RECAP_TTL_MS,
9455
9725
  verifyAnchor as verifyAnchor3,
9456
9726
  writeBriefingMarker as writeBriefingMarker2
9457
9727
  } from "@hivelore/core";
9458
9728
 
9459
9729
  // src/utils/claude-hooks.ts
9460
- import { existsSync as existsSync39 } from "fs";
9730
+ import { existsSync as existsSync40 } from "fs";
9461
9731
  import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile24 } from "fs/promises";
9462
- import path37 from "path";
9732
+ import path38 from "path";
9463
9733
  var HAIVE_HOOK_TAG = "haive-enforcement";
9464
9734
  var POST_TOOL_USE_GROUP = {
9465
9735
  matcher: "Edit|Write|Bash",
@@ -9545,7 +9815,7 @@ function unpatchClaudeSettings(input) {
9545
9815
  async function installClaudeHooksAtPath(settingsPath) {
9546
9816
  let raw = null;
9547
9817
  let created = false;
9548
- if (existsSync39(settingsPath)) {
9818
+ if (existsSync40(settingsPath)) {
9549
9819
  try {
9550
9820
  raw = JSON.parse(await readFile17(settingsPath, "utf8"));
9551
9821
  } catch {
@@ -9555,12 +9825,12 @@ async function installClaudeHooksAtPath(settingsPath) {
9555
9825
  created = true;
9556
9826
  }
9557
9827
  const patched = patchClaudeSettings(raw);
9558
- await mkdir15(path37.dirname(settingsPath), { recursive: true });
9828
+ await mkdir15(path38.dirname(settingsPath), { recursive: true });
9559
9829
  await writeFile24(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
9560
9830
  return { settingsPath, created };
9561
9831
  }
9562
9832
  async function uninstallClaudeHooksAtPath(settingsPath) {
9563
- if (!existsSync39(settingsPath)) {
9833
+ if (!existsSync40(settingsPath)) {
9564
9834
  return { settingsPath, created: false };
9565
9835
  }
9566
9836
  const raw = JSON.parse(await readFile17(settingsPath, "utf8"));
@@ -9571,9 +9841,9 @@ async function uninstallClaudeHooksAtPath(settingsPath) {
9571
9841
  function defaultClaudeSettingsPath(scope, projectRoot) {
9572
9842
  if (scope === "user") {
9573
9843
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
9574
- return path37.join(home, ".claude", "settings.json");
9844
+ return path38.join(home, ".claude", "settings.json");
9575
9845
  }
9576
- return path37.join(projectRoot, ".claude", "settings.local.json");
9846
+ return path38.join(projectRoot, ".claude", "settings.local.json");
9577
9847
  }
9578
9848
 
9579
9849
  // src/utils/command-sensors.ts
@@ -9653,8 +9923,52 @@ async function executeCommandSensors(specs, root) {
9653
9923
  return runs;
9654
9924
  }
9655
9925
 
9926
+ // src/utils/sensor-evaluations.ts
9927
+ import { execFile as execFile4 } from "child_process";
9928
+ import { promisify as promisify4 } from "util";
9929
+ import {
9930
+ computeScopeHash,
9931
+ sensorAppliesToPath
9932
+ } from "@hivelore/core";
9933
+ var exec3 = promisify4(execFile4);
9934
+ async function gitHeadSha(root) {
9935
+ try {
9936
+ const { stdout } = await exec3("git", ["rev-parse", "HEAD"], { cwd: root });
9937
+ return stdout.trim();
9938
+ } catch {
9939
+ return "";
9940
+ }
9941
+ }
9942
+ async function trackedFiles(root) {
9943
+ try {
9944
+ const { stdout } = await exec3("git", ["ls-files", "-co", "--exclude-standard"], { cwd: root });
9945
+ return stdout.split("\n").map((f) => f.trim()).filter(Boolean);
9946
+ } catch {
9947
+ return [];
9948
+ }
9949
+ }
9950
+ async function commandScopeHash(root, spec) {
9951
+ if (spec.paths.length === 0) return "";
9952
+ const sensor = { kind: spec.kind, paths: spec.paths };
9953
+ const files = (await trackedFiles(root)).filter((file) => sensorAppliesToPath(sensor, [], file));
9954
+ return computeScopeHash(root, files);
9955
+ }
9956
+ function evaluation(base, command) {
9957
+ return {
9958
+ at: base.at ?? (/* @__PURE__ */ new Date()).toISOString(),
9959
+ memory_id: base.memory_id,
9960
+ kind: base.kind,
9961
+ stage: base.stage,
9962
+ head_sha: base.head_sha,
9963
+ scope_hash: base.scope_hash,
9964
+ outcome: base.outcome,
9965
+ ...command?.exit_code !== null ? { exit_code: command?.exit_code } : {},
9966
+ ...command ? { duration_ms: command.duration_ms } : {}
9967
+ };
9968
+ }
9969
+
9656
9970
  // src/commands/enforce.ts
9657
- var execFileAsync2 = promisify4(execFile4);
9971
+ var execFileAsync2 = promisify5(execFile5);
9658
9972
  var MAX_STDIN_BYTES2 = 256 * 1024;
9659
9973
  var ENFORCE_HOOK_MARKER = "# Hivelore enforcement hook";
9660
9974
  function registerEnforce(program2) {
@@ -9715,7 +10029,7 @@ function registerEnforce(program2) {
9715
10029
  ui.success(`Removed Hivelore hooks from ${result.settingsPath}`);
9716
10030
  } else {
9717
10031
  const result = await installClaudeHooksAtPath(settingsPath);
9718
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path38.relative(root, result.settingsPath) || result.settingsPath})`);
10032
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path39.relative(root, result.settingsPath) || result.settingsPath})`);
9719
10033
  }
9720
10034
  } catch (err) {
9721
10035
  ui.warn(`Claude Code hooks not ${opts.removeClaude ? "removed" : "installed"}: ${err instanceof Error ? err.message : String(err)}`);
@@ -9737,19 +10051,19 @@ function registerEnforce(program2) {
9737
10051
  enforce.command("cleanup").description("Remove generated Hivelore runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
9738
10052
  const root = findProjectRoot37(opts.dir);
9739
10053
  const paths = resolveHaivePaths35(root);
9740
- const cacheDir = path38.join(paths.haiveDir, ".cache");
9741
- if (existsSync40(cacheDir)) {
9742
- if (opts.dryRun) ui.info(`would clean ${path38.relative(root, cacheDir)} (preserving .gitignore)`);
10054
+ const cacheDir = path39.join(paths.haiveDir, ".cache");
10055
+ if (existsSync41(cacheDir)) {
10056
+ if (opts.dryRun) ui.info(`would clean ${path39.relative(root, cacheDir)} (preserving .gitignore)`);
9743
10057
  else {
9744
10058
  const removed = await cleanupCacheDir(cacheDir);
9745
- ui.success(`cleaned ${path38.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
10059
+ ui.success(`cleaned ${path39.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
9746
10060
  }
9747
10061
  }
9748
- if (existsSync40(paths.runtimeDir)) {
9749
- if (opts.dryRun) ui.info(`would clean ${path38.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
10062
+ if (existsSync41(paths.runtimeDir)) {
10063
+ if (opts.dryRun) ui.info(`would clean ${path39.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
9750
10064
  else {
9751
10065
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
9752
- ui.success(`cleaned ${path38.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
10066
+ ui.success(`cleaned ${path39.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
9753
10067
  }
9754
10068
  }
9755
10069
  });
@@ -9793,7 +10107,7 @@ function registerEnforce(program2) {
9793
10107
  const root = resolveRoot(opts.dir, payload);
9794
10108
  if (!root) return;
9795
10109
  const paths = resolveHaivePaths35(root);
9796
- if (!existsSync40(paths.haiveDir)) return;
10110
+ if (!existsSync41(paths.haiveDir)) return;
9797
10111
  await mkdir16(paths.runtimeDir, { recursive: true });
9798
10112
  const sessionId = opts.sessionId ?? payload.session_id;
9799
10113
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this Hivelore-initialized project.";
@@ -9856,7 +10170,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
9856
10170
  const root = resolveRoot(opts.dir, payload);
9857
10171
  if (!root) return;
9858
10172
  const paths = resolveHaivePaths35(root);
9859
- if (!existsSync40(paths.haiveDir)) return;
10173
+ if (!existsSync41(paths.haiveDir)) return;
9860
10174
  if (!isWriteLikeTool(payload)) return;
9861
10175
  const config = await loadConfig12(paths);
9862
10176
  if (config.enforcement?.requireBriefingFirst === false) return;
@@ -9921,7 +10235,7 @@ function emitPreToolUseContext(text) {
9921
10235
  async function buildFinishReport(dir) {
9922
10236
  const root = findProjectRoot37(dir);
9923
10237
  const paths = resolveHaivePaths35(root);
9924
- const initialized = existsSync40(paths.haiveDir);
10238
+ const initialized = existsSync41(paths.haiveDir);
9925
10239
  const config = initialized ? await loadConfig12(paths) : {};
9926
10240
  const mode = config.enforcement?.mode ?? "strict";
9927
10241
  const findings = [];
@@ -10113,8 +10427,8 @@ async function buildFinishReport(dir) {
10113
10427
  async function checkFailureCapture(paths, config) {
10114
10428
  const gate = config.enforcement?.failureCaptureGate ?? "warn";
10115
10429
  if (gate === "off") return [];
10116
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
10117
- if (!existsSync40(obsFile)) return [];
10430
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
10431
+ if (!existsSync41(obsFile)) return [];
10118
10432
  const failures = [];
10119
10433
  try {
10120
10434
  const raw = await readFile18(obsFile, "utf8");
@@ -10131,7 +10445,7 @@ async function checkFailureCapture(paths, config) {
10131
10445
  return [];
10132
10446
  }
10133
10447
  if (failures.length === 0) return [];
10134
- const memories = existsSync40(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
10448
+ const memories = existsSync41(paths.memoriesDir) ? await loadMemoriesFromDir14(paths.memoriesDir) : [];
10135
10449
  const captureTimes = memories.filter(({ memory: memory2 }) => ["attempt", "gotcha"].includes(memory2.frontmatter.type)).map(({ memory: memory2 }) => memory2.frontmatter.created_at);
10136
10450
  const uncaptured = findUncapturedFailures(failures, captureTimes);
10137
10451
  if (uncaptured.length === 0) {
@@ -10166,7 +10480,7 @@ function finishReport(root, initialized, mode, findings, config) {
10166
10480
  async function runWithEnforcement(command, args, opts) {
10167
10481
  const root = findProjectRoot37(opts.dir);
10168
10482
  const paths = resolveHaivePaths35(root);
10169
- if (!existsSync40(paths.haiveDir)) {
10483
+ if (!existsSync41(paths.haiveDir)) {
10170
10484
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
10171
10485
  process.exit(1);
10172
10486
  }
@@ -10185,7 +10499,7 @@ async function runWithEnforcement(command, args, opts) {
10185
10499
  process.exit(2);
10186
10500
  }
10187
10501
  ui.info(`Hivelore briefing marker created for wrapped agent session: ${sessionId}`);
10188
- ui.info(`Briefing written to ${path38.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
10502
+ ui.info(`Briefing written to ${path39.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
10189
10503
  const child = spawn4(command, args, {
10190
10504
  cwd: root,
10191
10505
  stdio: "inherit",
@@ -10237,9 +10551,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
10237
10551
  source: "haive-run",
10238
10552
  memoryIds: briefing.memories.map((m) => m.id)
10239
10553
  });
10240
- const dir = path38.join(paths.runtimeDir, "enforcement", "briefings");
10554
+ const dir = path39.join(paths.runtimeDir, "enforcement", "briefings");
10241
10555
  await mkdir16(dir, { recursive: true });
10242
- const file = path38.join(dir, `${sessionId}.md`);
10556
+ const file = path39.join(dir, `${sessionId}.md`);
10243
10557
  const parts = [
10244
10558
  "# Hivelore Briefing",
10245
10559
  "",
@@ -10277,7 +10591,7 @@ async function checkBootstrapComplete(paths, config, productionCodeChanged) {
10277
10591
  projectContextRaw = await readFile18(paths.projectContext, "utf8");
10278
10592
  } catch {
10279
10593
  }
10280
- const memories = existsSync40(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
10594
+ const memories = existsSync41(paths.memoriesDir) ? await loadMemoriesFromDir14(paths.memoriesDir) : [];
10281
10595
  const codeMap = await loadCodeMap7(paths);
10282
10596
  const codeFiles = codeMap ? Object.keys(codeMap.files) : [];
10283
10597
  let existingModules = [];
@@ -10309,7 +10623,7 @@ ${renderBootstrapChecklist(assessment)}`,
10309
10623
  async function buildEnforcementReport(dir, stage, sessionId) {
10310
10624
  const root = findProjectRoot37(dir);
10311
10625
  const paths = resolveHaivePaths35(root);
10312
- const initialized = existsSync40(paths.haiveDir);
10626
+ const initialized = existsSync41(paths.haiveDir);
10313
10627
  const config = initialized ? await loadConfig12(paths) : {};
10314
10628
  if (initialized) {
10315
10629
  await applyLightweightRepairs(root, paths);
@@ -10343,7 +10657,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
10343
10657
  findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
10344
10658
  });
10345
10659
  }
10346
- findings.push(...await inspectIntegrationVersions(root, "0.33.0"));
10660
+ findings.push(...await inspectIntegrationVersions(root, "0.35.0"));
10347
10661
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
10348
10662
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
10349
10663
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
@@ -10423,7 +10737,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
10423
10737
  }];
10424
10738
  }
10425
10739
  const hasErrors = effectiveFindings.some((f) => f.severity === "error");
10426
- return withCategories({
10740
+ const report = withCategories({
10427
10741
  root,
10428
10742
  initialized,
10429
10743
  mode,
@@ -10432,6 +10746,18 @@ async function buildEnforcementReport(dir, stage, sessionId) {
10432
10746
  should_block: mode === "strict" && hasErrors,
10433
10747
  findings: effectiveFindings
10434
10748
  });
10749
+ if (!report.should_block && (stage === "pre-commit" || stage === "ci")) {
10750
+ const headSha = await gitHeadSha(root);
10751
+ await appendSensorEvaluations(paths, [evaluation({
10752
+ memory_id: "__gate__",
10753
+ kind: "shell",
10754
+ stage,
10755
+ head_sha: headSha,
10756
+ scope_hash: "",
10757
+ outcome: "silent"
10758
+ })]);
10759
+ }
10760
+ return report;
10435
10761
  }
10436
10762
  function withCategories(report) {
10437
10763
  return {
@@ -10446,8 +10772,8 @@ function withCategories(report) {
10446
10772
  async function hasRecentSessionRecap(paths) {
10447
10773
  const handoffAge = await handoffAgeMs(paths.root);
10448
10774
  if (handoffAge !== null && handoffAge < SESSION_RECAP_TTL_MS) return true;
10449
- if (!existsSync40(paths.memoriesDir)) return false;
10450
- const all = await loadMemoriesFromDir15(paths.memoriesDir);
10775
+ if (!existsSync41(paths.memoriesDir)) return false;
10776
+ const all = await loadMemoriesFromDir14(paths.memoriesDir);
10451
10777
  return all.some(({ memory: memory2 }) => {
10452
10778
  const fm = memory2.frontmatter;
10453
10779
  const freshnessDate = fm.verified_at ?? fm.created_at;
@@ -10455,8 +10781,8 @@ async function hasRecentSessionRecap(paths) {
10455
10781
  });
10456
10782
  }
10457
10783
  async function verifyMemoryPolicy(paths, config) {
10458
- if (!existsSync40(paths.memoriesDir)) return [];
10459
- const all = await loadMemoriesFromDir15(paths.memoriesDir);
10784
+ if (!existsSync41(paths.memoriesDir)) return [];
10785
+ const all = await loadMemoriesFromDir14(paths.memoriesDir);
10460
10786
  const findings = [];
10461
10787
  const staleImportant = [];
10462
10788
  let verified = 0;
@@ -10493,12 +10819,12 @@ async function verifyMemoryPolicy(paths, config) {
10493
10819
  return findings;
10494
10820
  }
10495
10821
  async function verifyDecisionCoverage(paths, stage, sessionId) {
10496
- if (!existsSync40(paths.memoriesDir)) return [];
10822
+ if (!existsSync41(paths.memoriesDir)) return [];
10497
10823
  const changedFiles = (await getChangedFiles(paths.root, stage)).filter((f) => !isGeneratedArtifact(f));
10498
10824
  if (changedFiles.length === 0) {
10499
10825
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
10500
10826
  }
10501
- const all = await loadMemoriesFromDir15(paths.memoriesDir);
10827
+ const all = await loadMemoriesFromDir14(paths.memoriesDir);
10502
10828
  const changedSet = new Set(changedFiles);
10503
10829
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention"]);
10504
10830
  const relevant = all.filter(({ memory: memory2 }) => {
@@ -10527,7 +10853,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
10527
10853
  const consulted = new Set(marker?.memory_ids ?? []);
10528
10854
  const missing = relevant.filter(({ memory: memory2, filePath }) => {
10529
10855
  if (consulted.has(memory2.frontmatter.id)) return false;
10530
- if (changedSet.has(path38.relative(paths.root, filePath))) return false;
10856
+ if (changedSet.has(path39.relative(paths.root, filePath))) return false;
10531
10857
  return true;
10532
10858
  }).map(({ memory: memory2 }) => memory2);
10533
10859
  if (missing.length === 0) {
@@ -10587,12 +10913,12 @@ async function runPrecommitPolicy(paths, gate, stage) {
10587
10913
  anchored_blocks,
10588
10914
  semantic: true
10589
10915
  }, { paths });
10590
- const sensorFindings = await runSensorGate(paths, snapshot.diff);
10916
+ const sensorFindings = await runSensorGate(paths, snapshot.diff, stage);
10591
10917
  const reviewWarnings = result.warnings.filter(
10592
10918
  (w) => w.level === "review" && !w.reasons.includes("sensor")
10593
10919
  );
10594
10920
  const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
10595
- const reviewSeenFile = path38.join(paths.runtimeDir, "enforcement", "review-seen.json");
10921
+ const reviewSeenFile = path39.join(paths.runtimeDir, "enforcement", "review-seen.json");
10596
10922
  let reviewSeen = {};
10597
10923
  try {
10598
10924
  reviewSeen = JSON.parse(await readFile18(reviewSeenFile, "utf8"));
@@ -10619,7 +10945,7 @@ async function runPrecommitPolicy(paths, gate, stage) {
10619
10945
  for (const [id, at] of Object.entries(reviewSeen)) {
10620
10946
  if (!Number.isFinite(Date.parse(at)) || now - Date.parse(at) >= REVIEW_SEEN_TTL_MS) delete reviewSeen[id];
10621
10947
  }
10622
- await mkdir16(path38.dirname(reviewSeenFile), { recursive: true });
10948
+ await mkdir16(path39.dirname(reviewSeenFile), { recursive: true });
10623
10949
  await writeFile25(reviewSeenFile, JSON.stringify(reviewSeen, null, 2), "utf8");
10624
10950
  } catch {
10625
10951
  }
@@ -10650,10 +10976,10 @@ async function runPrecommitPolicy(paths, gate, stage) {
10650
10976
  ...sensorFindings
10651
10977
  ];
10652
10978
  }
10653
- async function runSensorGate(paths, diff) {
10654
- if (!diff || !existsSync40(paths.memoriesDir)) return [];
10979
+ async function runSensorGate(paths, diff, stage) {
10980
+ if (!diff || !existsSync41(paths.memoriesDir)) return [];
10655
10981
  try {
10656
- const loaded = await loadMemoriesFromDir15(paths.memoriesDir);
10982
+ const loaded = await loadMemoriesFromDir14(paths.memoriesDir);
10657
10983
  const scannable = loaded.map((l) => l.memory).filter((m) => Boolean(m.frontmatter.sensor) && !isRetiredMemory2(m.frontmatter, m.body));
10658
10984
  if (scannable.length === 0) return [];
10659
10985
  const targets = sensorTargetsFromDiff(diff).filter((t) => isSensorScannablePath(t.path));
@@ -10661,8 +10987,22 @@ async function runSensorGate(paths, diff) {
10661
10987
  const findings = [];
10662
10988
  const seen = /* @__PURE__ */ new Set();
10663
10989
  const firedIds = /* @__PURE__ */ new Set();
10990
+ const ledgerRows = [];
10991
+ const headSha = await gitHeadSha(paths.root);
10664
10992
  const regexSensorMemories = scannable.filter((m) => m.frontmatter.sensor.kind === "regex");
10665
10993
  const hits = regexSensorMemories.length > 0 ? runSensors(regexSensorMemories, targets) : [];
10994
+ for (const memory2 of regexSensorMemories) {
10995
+ const sensor = memory2.frontmatter.sensor;
10996
+ if (!targets.some((target) => sensorAppliesToPath2(sensor, memory2.frontmatter.anchor.paths, target.path))) continue;
10997
+ ledgerRows.push(evaluation({
10998
+ memory_id: memory2.frontmatter.id,
10999
+ kind: "regex",
11000
+ stage,
11001
+ head_sha: headSha,
11002
+ scope_hash: "",
11003
+ outcome: hits.some((hit) => hit.memory_id === memory2.frontmatter.id) ? "fired" : "silent"
11004
+ }));
11005
+ }
10666
11006
  for (const hit of hits) {
10667
11007
  if (seen.has(hit.memory_id)) continue;
10668
11008
  seen.add(hit.memory_id);
@@ -10674,7 +11014,8 @@ async function runSensorGate(paths, diff) {
10674
11014
  code: "sensor-block",
10675
11015
  message: `Block sensor fired \u2014 ${hit.memory_id}: ${hit.message}${where}`,
10676
11016
  fix: "Remove the flagged pattern, or run `hivelore sensors check` to inspect the match.",
10677
- impact: 45
11017
+ impact: 45,
11018
+ memory_ids: [hit.memory_id]
10678
11019
  });
10679
11020
  } else {
10680
11021
  findings.push({
@@ -10682,7 +11023,8 @@ async function runSensorGate(paths, diff) {
10682
11023
  code: "sensor-warn",
10683
11024
  message: `Sensor flagged ${hit.memory_id}: ${hit.message}${where}`,
10684
11025
  fix: "Review the flagged line; `hivelore sensors check` shows the matched code.",
10685
- impact: 5
11026
+ impact: 5,
11027
+ memory_ids: [hit.memory_id]
10686
11028
  });
10687
11029
  }
10688
11030
  }
@@ -10690,7 +11032,34 @@ async function runSensorGate(paths, diff) {
10690
11032
  if (config?.enforcement?.runCommandSensors === true) {
10691
11033
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
10692
11034
  const specs = selectCommandSensors(scannable, changedPaths).filter((sp) => !seen.has(sp.memory_id));
10693
- for (const run of await executeCommandSensors(specs, paths.root)) {
11035
+ const runs = await executeCommandSensors(specs, paths.root);
11036
+ for (const run of runs) {
11037
+ const spec = specs.find((candidate) => candidate.memory_id === run.memory_id);
11038
+ ledgerRows.push(evaluation({
11039
+ memory_id: run.memory_id,
11040
+ kind: run.kind,
11041
+ stage,
11042
+ head_sha: headSha,
11043
+ scope_hash: await commandScopeHash(paths.root, spec),
11044
+ outcome: run.status === "failed" ? "fired" : run.status === "passed" ? "silent" : "unrunnable"
11045
+ }, { exit_code: run.exit_code, duration_ms: run.duration_ms }));
11046
+ }
11047
+ const prior = await loadSensorLedger3(paths);
11048
+ const health = new Map(assessSensorHealth3([...prior, ...ledgerRows]).map((h) => [h.memory_id, h]));
11049
+ for (const run of runs) {
11050
+ const sensorHealth = health.get(run.memory_id);
11051
+ const quarantined = sensorHealth?.quarantine_pending === true;
11052
+ if (quarantined && run.severity === "block") {
11053
+ const last = sensorHealth.flaps.at(-1);
11054
+ findings.push({
11055
+ severity: "warn",
11056
+ code: "sensor-flaky",
11057
+ message: `Command sensor ${run.memory_id} flapped ${sensorHealth.flap_count}\xD7 on identical inputs; treated as warn pending sync quarantine. Last contradiction: ${last.previous.at} ${last.previous.outcome} \u2192 ${last.current.at} ${last.current.outcome}.`,
11058
+ fix: "Run `hivelore sync`, fix the flaky oracle, then re-promote with `hivelore sensors promote <id> --yes`.",
11059
+ impact: 5,
11060
+ memory_ids: [run.memory_id]
11061
+ });
11062
+ }
10694
11063
  if (run.status === "passed") continue;
10695
11064
  seen.add(run.memory_id);
10696
11065
  if (run.status === "unrunnable") {
@@ -10707,14 +11076,15 @@ ${run.output_tail}` : ""),
10707
11076
  firedIds.add(run.memory_id);
10708
11077
  const outputBlock = run.output_tail ? `
10709
11078
  ${run.output_tail}` : "";
10710
- if (run.severity === "block") {
11079
+ if (run.severity === "block" && !quarantined) {
10711
11080
  findings.push({
10712
11081
  severity: "error",
10713
11082
  code: "sensor-block",
10714
11083
  message: `Block ${run.kind} sensor fired \u2014 ${run.memory_id}: ${run.message}
10715
11084
  command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlock}`,
10716
11085
  fix: "Fix the behaviour the command checks, or run `hivelore sensors check --commands` to inspect it.",
10717
- impact: 45
11086
+ impact: 45,
11087
+ memory_ids: [run.memory_id]
10718
11088
  });
10719
11089
  } else {
10720
11090
  findings.push({
@@ -10722,13 +11092,19 @@ command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlo
10722
11092
  code: "sensor-warn",
10723
11093
  message: `${run.kind} sensor flagged ${run.memory_id}: ${run.message} (exit ${run.exit_code})${outputBlock}`,
10724
11094
  fix: "Review the failing command; `hivelore sensors check --commands` re-runs it.",
10725
- impact: 5
11095
+ impact: 5,
11096
+ memory_ids: [run.memory_id]
10726
11097
  });
10727
11098
  }
10728
11099
  }
10729
11100
  }
11101
+ await appendSensorEvaluations(paths, ledgerRows);
10730
11102
  if (firedIds.size > 0) {
10731
- await recordPreventionHits(paths, [...firedIds], "sensor").catch(() => {
11103
+ const details = Object.fromEntries([...firedIds].map((id) => {
11104
+ const row = ledgerRows.find((entry) => entry.memory_id === id && entry.outcome === "fired");
11105
+ return [id, { kind: row?.kind ?? "regex", stage, ...row?.exit_code !== void 0 ? { exit_code: row.exit_code } : {} }];
11106
+ }));
11107
+ await recordPreventionHits(paths, [...firedIds], "sensor", /* @__PURE__ */ new Date(), details).catch(() => {
10732
11108
  });
10733
11109
  }
10734
11110
  return findings;
@@ -10766,16 +11142,16 @@ async function cleanupRuntimeDir(runtimeDir) {
10766
11142
  for (const entry of entries) {
10767
11143
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
10768
11144
  if (entry.name === "enforcement") {
10769
- removed += await cleanupEnforcementDir(path38.join(runtimeDir, entry.name));
11145
+ removed += await cleanupEnforcementDir(path39.join(runtimeDir, entry.name));
10770
11146
  continue;
10771
11147
  }
10772
- await rm2(path38.join(runtimeDir, entry.name), { recursive: true, force: true });
11148
+ await rm2(path39.join(runtimeDir, entry.name), { recursive: true, force: true });
10773
11149
  removed++;
10774
11150
  }
10775
- await writeFile25(path38.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
10776
- if (!existsSync40(path38.join(runtimeDir, "README.md"))) {
11151
+ await writeFile25(path39.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
11152
+ if (!existsSync41(path39.join(runtimeDir, "README.md"))) {
10777
11153
  await writeFile25(
10778
- path38.join(runtimeDir, "README.md"),
11154
+ path39.join(runtimeDir, "README.md"),
10779
11155
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. Hivelore cleanup preserves briefing markers so enforcement state remains valid.\n",
10780
11156
  "utf8"
10781
11157
  );
@@ -10788,10 +11164,10 @@ async function cleanupCacheDir(cacheDir) {
10788
11164
  const entries = await readdir4(cacheDir, { withFileTypes: true }).catch(() => []);
10789
11165
  for (const entry of entries) {
10790
11166
  if (entry.name === ".gitignore") continue;
10791
- await rm2(path38.join(cacheDir, entry.name), { recursive: true, force: true });
11167
+ await rm2(path39.join(cacheDir, entry.name), { recursive: true, force: true });
10792
11168
  removed++;
10793
11169
  }
10794
- await writeFile25(path38.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
11170
+ await writeFile25(path39.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
10795
11171
  return removed;
10796
11172
  }
10797
11173
  async function cleanupEnforcementDir(enforcementDir) {
@@ -10799,7 +11175,7 @@ async function cleanupEnforcementDir(enforcementDir) {
10799
11175
  const entries = await readdir4(enforcementDir, { withFileTypes: true }).catch(() => []);
10800
11176
  for (const entry of entries) {
10801
11177
  if (entry.name === "briefings") continue;
10802
- await rm2(path38.join(enforcementDir, entry.name), { recursive: true, force: true });
11178
+ await rm2(path39.join(enforcementDir, entry.name), { recursive: true, force: true });
10803
11179
  removed++;
10804
11180
  }
10805
11181
  return removed;
@@ -10817,8 +11193,8 @@ async function inspectIntegrationVersions(root, expectedVersion) {
10817
11193
  const missingBins = /* @__PURE__ */ new Map();
10818
11194
  const staleBins = /* @__PURE__ */ new Map();
10819
11195
  for (const rel of files) {
10820
- const file = path38.join(root, rel);
10821
- if (!existsSync40(file)) continue;
11196
+ const file = path39.join(root, rel);
11197
+ if (!existsSync41(file)) continue;
10822
11198
  const text = await readFile18(file, "utf8").catch(() => "");
10823
11199
  for (const bin of extractAbsoluteHaiveBins2(text)) {
10824
11200
  const version = versionForBinary2(bin);
@@ -10935,7 +11311,7 @@ async function resolveCiDiffRange(root) {
10935
11311
  }
10936
11312
  async function resolveGithubEventRange(root) {
10937
11313
  const eventPath = process.env.GITHUB_EVENT_PATH;
10938
- if (!eventPath || !existsSync40(eventPath)) return null;
11314
+ if (!eventPath || !existsSync41(eventPath)) return null;
10939
11315
  try {
10940
11316
  const event = JSON.parse(await readFile18(eventPath, "utf8"));
10941
11317
  const prBase = cleanGitSha(event.pull_request?.base?.sha);
@@ -11040,7 +11416,7 @@ function isShippablePath(file) {
11040
11416
  }
11041
11417
  var CI_SKIP_DIRECTIVE = /\[skip ci\]|\[ci skip\]|\[no ci\]|\[skip actions\]|\*\*\*NO_CI\*\*\*|skip-checks: *true/i;
11042
11418
  async function checkCommitMessageSkipCi(root, msgfile) {
11043
- const file = path38.isAbsolute(msgfile) ? msgfile : path38.join(root, msgfile);
11419
+ const file = path39.isAbsolute(msgfile) ? msgfile : path39.join(root, msgfile);
11044
11420
  const raw = await readFile18(file, "utf8").catch(() => "");
11045
11421
  const cleaned = raw.split("\n").filter((line) => !line.startsWith("#")).join("\n");
11046
11422
  if (!CI_SKIP_DIRECTIVE.test(cleaned)) return { block: false, message: "" };
@@ -11069,7 +11445,7 @@ async function inspectReleaseVersionState(root, upstream) {
11069
11445
  }
11070
11446
  async function readPackageVersion(root, relPath) {
11071
11447
  try {
11072
- const data = JSON.parse(await readFile18(path38.join(root, relPath), "utf8"));
11448
+ const data = JSON.parse(await readFile18(path39.join(root, relPath), "utf8"));
11073
11449
  return typeof data.version === "string" ? data.version : void 0;
11074
11450
  } catch {
11075
11451
  return void 0;
@@ -11257,8 +11633,8 @@ function buildScore(findings, threshold = 80) {
11257
11633
  };
11258
11634
  }
11259
11635
  async function installGitEnforcement(root) {
11260
- const hooksDir = path38.join(root, ".git", "hooks");
11261
- if (!existsSync40(path38.join(root, ".git"))) {
11636
+ const hooksDir = path39.join(root, ".git", "hooks");
11637
+ if (!existsSync41(path39.join(root, ".git"))) {
11262
11638
  ui.warn("No .git directory found; git enforcement hooks skipped.");
11263
11639
  return;
11264
11640
  }
@@ -11314,8 +11690,8 @@ _hivelore sync --quiet --since ORIG_HEAD || true
11314
11690
  }
11315
11691
  ];
11316
11692
  for (const hook of hooks) {
11317
- const file = path38.join(hooksDir, hook.name);
11318
- if (existsSync40(file)) {
11693
+ const file = path39.join(hooksDir, hook.name);
11694
+ if (existsSync41(file)) {
11319
11695
  const current = await readFile18(file, "utf8").catch(() => "");
11320
11696
  if (current.includes(ENFORCE_HOOK_MARKER)) {
11321
11697
  await writeFile25(file, hook.body, "utf8");
@@ -11332,13 +11708,29 @@ ${hook.body}`, "utf8");
11332
11708
  ui.success("Installed git hooks: pre-commit, pre-push, commit-msg (blocking) + post-merge, post-rewrite (sync)");
11333
11709
  }
11334
11710
  async function installCiEnforcement(root) {
11335
- const workflowPath = path38.join(root, ".github", "workflows", "haive-enforcement.yml");
11336
- await mkdir16(path38.dirname(workflowPath), { recursive: true });
11337
- if (existsSync40(workflowPath)) {
11338
- ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
11711
+ const workflowPath = path39.join(root, ".github", "workflows", "haive-enforcement.yml");
11712
+ await mkdir16(path39.dirname(workflowPath), { recursive: true });
11713
+ const workflow = renderCiEnforcementWorkflow();
11714
+ if (existsSync41(workflowPath)) {
11715
+ const existing = await readFile18(workflowPath, "utf8");
11716
+ const start = "# haive:enforcement-workflow:start";
11717
+ const end = "# haive:enforcement-workflow:end";
11718
+ const startAt = existing.indexOf(start);
11719
+ const endAt = existing.indexOf(end);
11720
+ if (startAt >= 0 && endAt > startAt) {
11721
+ await writeFile25(workflowPath, existing.slice(0, startAt) + workflow + existing.slice(endAt + end.length), "utf8");
11722
+ ui.success(`Updated ${path39.relative(root, workflowPath)} managed block`);
11723
+ } else {
11724
+ ui.info("GitHub Actions enforcement workflow already exists without Hivelore markers \u2014 preserved");
11725
+ }
11339
11726
  return;
11340
11727
  }
11341
- await writeFile25(workflowPath, `name: haive-enforcement
11728
+ await writeFile25(workflowPath, workflow, "utf8");
11729
+ ui.success(`Created ${path39.relative(root, workflowPath)}`);
11730
+ }
11731
+ function renderCiEnforcementWorkflow() {
11732
+ return `# haive:enforcement-workflow:start
11733
+ name: haive-enforcement
11342
11734
 
11343
11735
  on:
11344
11736
  pull_request:
@@ -11350,6 +11742,7 @@ jobs:
11350
11742
  runs-on: ubuntu-latest
11351
11743
  permissions:
11352
11744
  contents: read
11745
+ pull-requests: write
11353
11746
  steps:
11354
11747
  - uses: actions/checkout@v4
11355
11748
  with:
@@ -11360,12 +11753,51 @@ jobs:
11360
11753
  - name: Install Hivelore
11361
11754
  run: npm install -g @hivelore/cli
11362
11755
  - name: Enforce Hivelore policy
11756
+ id: gate
11363
11757
  env:
11364
11758
  HAIVE_BASE_SHA: \${{ github.event.pull_request.base.sha || github.event.before }}
11365
11759
  HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
11366
- run: hivelore enforce ci
11367
- `, "utf8");
11368
- ui.success(`Created ${path38.relative(root, workflowPath)}`);
11760
+ run: |
11761
+ set +e
11762
+ hivelore enforce ci --json > "$RUNNER_TEMP/haive-gate.json"
11763
+ echo "exit_code=$?" >> "$GITHUB_OUTPUT"
11764
+ exit 0
11765
+ - name: Upsert prevention receipt
11766
+ if: always() && github.event_name == 'pull_request'
11767
+ env:
11768
+ GH_TOKEN: \${{ github.token }}
11769
+ PR_NUMBER: \${{ github.event.pull_request.number }}
11770
+ run: |
11771
+ if [ -z "\${GH_TOKEN:-}" ] || ! command -v gh >/dev/null 2>&1; then exit 0; fi
11772
+ receipt="$(hivelore stats receipt --since 7d --json 2>/dev/null)" || exit 0
11773
+ gate="$(cat "$RUNNER_TEMP/haive-gate.json" 2>/dev/null)" || gate='{"findings":[]}'
11774
+ body="$(jq -nr --arg marker '<!-- haive:prevention-receipt -->' --argjson receipt "$receipt" --argjson gate "$gate" '
11775
+ $marker + "
11776
+ ## Hivelore prevention receipt
11777
+
11778
+ " +
11779
+ (([$gate.findings[]? | select(.code == "sensor-block" or .code == "sensor-warn") |
11780
+ "- **" + (.memory_ids[0] // "sensor") + "** \u2014 " + .message] | if length == 0 then
11781
+ "No documented sensor fired on this PR." else "### Fired on this PR
11782
+ " + join("
11783
+ ") end)) +
11784
+ "
11785
+
11786
+ Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **" +
11787
+ ($receipt.previous_total|tostring) + "**."
11788
+ ')" || exit 0
11789
+ comments="$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --paginate 2>/dev/null)" || exit 0
11790
+ comment_id="$(printf '%s' "$comments" | jq -r '.[] | select(.body | contains("<!-- haive:prevention-receipt -->")) | .id' | head -1)"
11791
+ if [ -n "$comment_id" ]; then
11792
+ gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" -f body="$body" >/dev/null 2>&1 || true
11793
+ else
11794
+ gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" -f body="$body" >/dev/null 2>&1 || true
11795
+ fi
11796
+ - name: Fail when enforcement blocked
11797
+ if: steps.gate.outputs.exit_code != '0'
11798
+ run: exit \${{ steps.gate.outputs.exit_code }}
11799
+ # haive:enforcement-workflow:end
11800
+ `;
11369
11801
  }
11370
11802
  function printReport(report, json, explain = false) {
11371
11803
  if (json) {
@@ -11469,15 +11901,15 @@ function extractToolPaths(payload, root) {
11469
11901
  }
11470
11902
  function normalizeToolPath(file, root) {
11471
11903
  const normalized = file.replace(/\\/g, "/");
11472
- if (!path38.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
11473
- return path38.relative(root, normalized).replace(/\\/g, "/");
11904
+ if (!path39.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
11905
+ return path39.relative(root, normalized).replace(/\\/g, "/");
11474
11906
  }
11475
11907
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
11476
- if (!existsSync40(paths.memoriesDir)) return [];
11908
+ if (!existsSync41(paths.memoriesDir)) return [];
11477
11909
  const marker = await readRecentBriefingMarker(paths, sessionId);
11478
11910
  const consulted = new Set(marker?.memory_ids ?? []);
11479
11911
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
11480
- const all = await loadMemoriesFromDir15(paths.memoriesDir);
11912
+ const all = await loadMemoriesFromDir14(paths.memoriesDir);
11481
11913
  return all.filter(({ memory: memory2 }) => {
11482
11914
  const fm = memory2.frontmatter;
11483
11915
  if (!policyTypes.has(fm.type)) return false;
@@ -11518,7 +11950,7 @@ async function readStdin2(maxBytes) {
11518
11950
  }
11519
11951
  var ATOMIC_STAGE_EXCLUDE = ["/.usage/", "/.runtime/", "/.cache/"];
11520
11952
  async function stageResyncedArtifacts(root, paths) {
11521
- const aiRel = path38.relative(root, paths.haiveDir);
11953
+ const aiRel = path39.relative(root, paths.haiveDir);
11522
11954
  const out = await runCommand2("git", ["diff", "--name-only", "--", aiRel], root).catch(() => "");
11523
11955
  const toStage = out.split("\n").map((line) => line.trim()).filter(Boolean).filter((file) => !ATOMIC_STAGE_EXCLUDE.some((excl) => `/${file}`.includes(excl)));
11524
11956
  if (toStage.length === 0) return;
@@ -11545,14 +11977,14 @@ function runCommand2(cmd, args, cwd) {
11545
11977
  }
11546
11978
 
11547
11979
  // src/commands/release.ts
11548
- import { existsSync as existsSync41 } from "fs";
11980
+ import { existsSync as existsSync42 } from "fs";
11549
11981
  import { readFile as readFile19, writeFile as writeFile26 } from "fs/promises";
11550
- import path39 from "path";
11982
+ import path40 from "path";
11551
11983
  import "commander";
11552
11984
  import { findProjectRoot as findProjectRoot38 } from "@hivelore/core";
11553
- import { execFile as execFile5 } from "child_process";
11554
- import { promisify as promisify5 } from "util";
11555
- var exec3 = promisify5(execFile5);
11985
+ import { execFile as execFile6 } from "child_process";
11986
+ import { promisify as promisify6 } from "util";
11987
+ var exec4 = promisify6(execFile6);
11556
11988
  var VERSION_FILES2 = [
11557
11989
  "package.json",
11558
11990
  "packages/core/package.json",
@@ -11561,7 +11993,7 @@ var VERSION_FILES2 = [
11561
11993
  "packages/embeddings/package.json"
11562
11994
  ];
11563
11995
  async function readCurrentVersion(root) {
11564
- const pkg = JSON.parse(await readFile19(path39.join(root, "package.json"), "utf8"));
11996
+ const pkg = JSON.parse(await readFile19(path40.join(root, "package.json"), "utf8"));
11565
11997
  if (!pkg.version) throw new Error("Root package.json has no version field.");
11566
11998
  return pkg.version;
11567
11999
  }
@@ -11582,8 +12014,8 @@ function registerRelease(program2) {
11582
12014
  const current = await readCurrentVersion(root);
11583
12015
  const next = nextVersion(current, spec);
11584
12016
  for (const rel of VERSION_FILES2) {
11585
- const file = path39.join(root, rel);
11586
- if (!existsSync41(file)) {
12017
+ const file = path40.join(root, rel);
12018
+ if (!existsSync42(file)) {
11587
12019
  ui.warn(`skip ${rel} (missing)`);
11588
12020
  continue;
11589
12021
  }
@@ -11596,8 +12028,8 @@ function registerRelease(program2) {
11596
12028
  }
11597
12029
  await writeFile26(file, updated, "utf8");
11598
12030
  }
11599
- const changelog = path39.join(root, "CHANGELOG.md");
11600
- if (existsSync41(changelog)) {
12031
+ const changelog = path40.join(root, "CHANGELOG.md");
12032
+ if (existsSync42(changelog)) {
11601
12033
  const raw = await readFile19(changelog, "utf8");
11602
12034
  const heading = `## [${next}]${opts.title ? ` \u2014 ${opts.title}` : ""}`;
11603
12035
  if (!raw.includes(`## [${next}]`)) {
@@ -11620,8 +12052,8 @@ ${heading}
11620
12052
  const root = findProjectRoot38(opts.dir);
11621
12053
  const version = await readCurrentVersion(root);
11622
12054
  for (const rel of VERSION_FILES2.slice(1)) {
11623
- const file = path39.join(root, rel);
11624
- if (!existsSync41(file)) continue;
12055
+ const file = path40.join(root, rel);
12056
+ if (!existsSync42(file)) continue;
11625
12057
  const v = JSON.parse(await readFile19(file, "utf8")).version;
11626
12058
  if (v !== version) {
11627
12059
  ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
@@ -11629,24 +12061,24 @@ ${heading}
11629
12061
  return;
11630
12062
  }
11631
12063
  }
11632
- const dirty = (await exec3("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
12064
+ const dirty = (await exec4("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
11633
12065
  if (dirty.length > 0) {
11634
12066
  ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
11635
12067
  process.exitCode = 1;
11636
12068
  return;
11637
12069
  }
11638
12070
  const tag = `v${version}`;
11639
- const existing = (await exec3("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
12071
+ const existing = (await exec4("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
11640
12072
  if (existing) {
11641
12073
  ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
11642
12074
  process.exitCode = 1;
11643
12075
  return;
11644
12076
  }
11645
- await exec3("git", ["tag", tag], { cwd: root });
12077
+ await exec4("git", ["tag", tag], { cwd: root });
11646
12078
  ui.success(`Created ${tag} at HEAD.`);
11647
12079
  if (opts.push !== false) {
11648
- await exec3("git", ["push"], { cwd: root });
11649
- await exec3("git", ["push", "origin", tag], { cwd: root });
12080
+ await exec4("git", ["push"], { cwd: root });
12081
+ await exec4("git", ["push", "origin", tag], { cwd: root });
11650
12082
  ui.success(`Pushed branch and ${tag}.`);
11651
12083
  ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
11652
12084
  }
@@ -11666,29 +12098,34 @@ function registerRun(program2) {
11666
12098
  }
11667
12099
 
11668
12100
  // src/commands/sensors.ts
11669
- import { execFile as execFile6 } from "child_process";
11670
- import { existsSync as existsSync42 } from "fs";
12101
+ import { execFile as execFile7 } from "child_process";
12102
+ import { existsSync as existsSync43 } from "fs";
11671
12103
  import { chmod as chmod2, mkdir as mkdir17, readFile as readFile20, writeFile as writeFile27 } from "fs/promises";
11672
- import path40 from "path";
11673
- import { promisify as promisify6 } from "util";
12104
+ import path41 from "path";
12105
+ import { promisify as promisify7 } from "util";
11674
12106
  import "commander";
11675
12107
  import {
11676
12108
  extractSensorExamples,
12109
+ appendSensorEvaluations as appendSensorEvaluations2,
12110
+ assessSensorHealth as assessSensorHealth4,
11677
12111
  findProjectRoot as findProjectRoot39,
11678
12112
  isRetiredMemory as isRetiredMemory3,
11679
12113
  judgeProposedSensor,
11680
12114
  loadConfig as loadConfig13,
11681
- loadMemoriesFromDir as loadMemoriesFromDir16,
12115
+ loadSensorLedger as loadSensorLedger4,
12116
+ loadMemoriesFromDir as loadMemoriesFromDir15,
11682
12117
  recordPreventionHits as recordPreventionHits2,
11683
12118
  resolveHaivePaths as resolveHaivePaths36,
11684
12119
  runSensors as runSensors2,
11685
12120
  selectCommandSensors as selectCommandSensors2,
11686
12121
  sensorPatternBrittleness as sensorPatternBrittleness2,
11687
12122
  sensorSelfCheck as sensorSelfCheck2,
12123
+ sensorAppliesToPath as sensorAppliesToPath3,
11688
12124
  scannableSensorTargets,
11689
- serializeMemory as serializeMemory15
12125
+ serializeMemory as serializeMemory15,
12126
+ withoutQuarantineNote
11690
12127
  } from "@hivelore/core";
11691
- var exec4 = promisify6(execFile6);
12128
+ var exec5 = promisify7(execFile7);
11692
12129
  function registerSensors(program2) {
11693
12130
  const sensors = program2.command("sensors").description("Operate executable sensors derived from Hivelore memories");
11694
12131
  sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
@@ -11724,7 +12161,7 @@ function registerSensors(program2) {
11724
12161
  const root = findProjectRoot39(opts.dir);
11725
12162
  const paths = resolveHaivePaths36(root);
11726
12163
  const memories = await runnableSensorMemories(paths);
11727
- const diff = opts.diffFile ? await readFile20(path40.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
12164
+ const diff = opts.diffFile ? await readFile20(path41.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
11728
12165
  const targets = scannableSensorTargets(diff);
11729
12166
  const hits = runSensors2(memories, targets);
11730
12167
  const config = await loadConfig13(paths);
@@ -11735,12 +12172,44 @@ function registerSensors(program2) {
11735
12172
  const commandHits = [];
11736
12173
  const commandUnrunnable = [];
11737
12174
  const commandSkipped = [];
12175
+ const ledgerRows = [];
12176
+ const headSha = await gitHeadSha(root);
12177
+ for (const memory2 of memories) {
12178
+ const sensor = memory2.frontmatter.sensor;
12179
+ if (!sensor || sensor.kind !== "regex") continue;
12180
+ const anchors = memory2.frontmatter.anchor.paths;
12181
+ const applicable = targets.some((target) => sensorAppliesToPath3(sensor, anchors, target.path));
12182
+ if (!applicable) continue;
12183
+ ledgerRows.push(evaluation({
12184
+ memory_id: memory2.frontmatter.id,
12185
+ kind: "regex",
12186
+ stage: "manual",
12187
+ head_sha: headSha,
12188
+ scope_hash: "",
12189
+ outcome: hits.some((hit) => hit.memory_id === memory2.frontmatter.id) ? "fired" : "silent"
12190
+ }));
12191
+ }
11738
12192
  if (commandSpecs.length > 0 && runCommands) {
11739
- for (const run of await executeCommandSensors(commandSpecs, root)) {
12193
+ const runs = await executeCommandSensors(commandSpecs, root);
12194
+ for (const run of runs) {
12195
+ const spec = commandSpecs.find((candidate) => candidate.memory_id === run.memory_id);
12196
+ ledgerRows.push(evaluation({
12197
+ memory_id: run.memory_id,
12198
+ kind: run.kind,
12199
+ stage: "manual",
12200
+ head_sha: headSha,
12201
+ scope_hash: await commandScopeHash(root, spec),
12202
+ outcome: run.status === "failed" ? "fired" : run.status === "passed" ? "silent" : "unrunnable"
12203
+ }, { exit_code: run.exit_code, duration_ms: run.duration_ms }));
12204
+ }
12205
+ const prior = await loadSensorLedger4(paths);
12206
+ const health = new Map(assessSensorHealth4([...prior, ...ledgerRows]).map((h) => [h.memory_id, h]));
12207
+ for (const run of runs) {
12208
+ const quarantined = health.get(run.memory_id)?.quarantine_pending === true;
11740
12209
  if (run.status === "failed") {
11741
12210
  commandHits.push({
11742
12211
  memory_id: run.memory_id,
11743
- severity: run.severity,
12212
+ severity: quarantined ? "warn" : run.severity,
11744
12213
  message: run.message,
11745
12214
  matched_line: `command failed (exit ${run.exit_code}, ${run.duration_ms}ms): ${run.command}`,
11746
12215
  ...run.output_tail ? { output_tail: run.output_tail } : {}
@@ -11752,8 +12221,17 @@ function registerSensors(program2) {
11752
12221
  } else if (commandSpecs.length > 0) {
11753
12222
  for (const spec of commandSpecs) commandSkipped.push(spec.memory_id);
11754
12223
  }
12224
+ await appendSensorEvaluations2(paths, ledgerRows);
11755
12225
  const firedIds = [...new Set([...hits, ...commandHits].map((hit) => hit.memory_id))];
11756
- await recordPreventionHits2(paths, firedIds, "sensor");
12226
+ const preventionDetails = Object.fromEntries([
12227
+ ...hits.map((hit) => [hit.memory_id, { kind: "regex", stage: "manual" }]),
12228
+ ...commandHits.map((hit) => [hit.memory_id, {
12229
+ kind: commandSpecs.find((spec) => spec.memory_id === hit.memory_id)?.kind ?? "shell",
12230
+ stage: "manual",
12231
+ exit_code: Number(/exit (\d+)/.exec(hit.matched_line)?.[1] ?? 1)
12232
+ }])
12233
+ ]);
12234
+ await recordPreventionHits2(paths, firedIds, "sensor", /* @__PURE__ */ new Date(), preventionDetails);
11757
12235
  const output = {
11758
12236
  scanned: memories.length,
11759
12237
  hits: hits.map((hit) => ({
@@ -11812,7 +12290,7 @@ function registerSensors(program2) {
11812
12290
  }
11813
12291
  const root = findProjectRoot39(opts.dir);
11814
12292
  const paths = resolveHaivePaths36(root);
11815
- const loaded = existsSync42(paths.memoriesDir) ? await loadMemoriesFromDir16(paths.memoriesDir) : [];
12293
+ const loaded = existsSync43(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
11816
12294
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
11817
12295
  if (!found) {
11818
12296
  ui.error(`No memory found with id ${id}`);
@@ -11860,7 +12338,7 @@ function registerSensors(program2) {
11860
12338
  ...found.memory.frontmatter,
11861
12339
  sensor: { ...sensor, severity }
11862
12340
  },
11863
- body: found.memory.body
12341
+ body: severity === "block" ? withoutQuarantineNote(found.memory.body) : found.memory.body
11864
12342
  };
11865
12343
  await writeFile27(found.filePath, serializeMemory15(next), "utf8");
11866
12344
  ui.success(`Updated ${id}: sensor severity=${severity}`);
@@ -11877,7 +12355,7 @@ function registerSensors(program2) {
11877
12355
  return;
11878
12356
  }
11879
12357
  const root2 = findProjectRoot39(opts.dir);
11880
- const { proposeSensor } = await import("./server-LZKYO5O5.js");
12358
+ const { proposeSensor } = await import("./server-2FUYM6KI.js");
11881
12359
  const out = await proposeSensor(
11882
12360
  {
11883
12361
  memory_id: id,
@@ -11920,7 +12398,7 @@ function registerSensors(program2) {
11920
12398
  }
11921
12399
  const root = findProjectRoot39(opts.dir);
11922
12400
  const paths = resolveHaivePaths36(root);
11923
- const loaded = existsSync42(paths.memoriesDir) ? await loadMemoriesFromDir16(paths.memoriesDir) : [];
12401
+ const loaded = existsSync43(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
11924
12402
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
11925
12403
  if (!found) {
11926
12404
  ui.error(`No memory found with id ${id}`);
@@ -11974,13 +12452,13 @@ function registerSensors(program2) {
11974
12452
  const root = findProjectRoot39(opts.dir);
11975
12453
  const paths = resolveHaivePaths36(root);
11976
12454
  const rows = await sensorRows(paths);
11977
- const outDir = path40.resolve(root, opts.outDir ?? ".ai/generated");
12455
+ const outDir = path41.resolve(root, opts.outDir ?? ".ai/generated");
11978
12456
  await mkdir17(outDir, { recursive: true });
11979
- const outPath = path40.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
12457
+ const outPath = path41.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
11980
12458
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
11981
12459
  await writeFile27(outPath, content, "utf8");
11982
12460
  if (format === "grep") await chmod2(outPath, 493);
11983
- ui.success(`Exported ${rows.length} sensor(s): ${path40.relative(root, outPath)}`);
12461
+ ui.success(`Exported ${rows.length} sensor(s): ${path41.relative(root, outPath)}`);
11984
12462
  });
11985
12463
  }
11986
12464
  function deriveProposedMessage(body, pattern, absent) {
@@ -12013,8 +12491,8 @@ async function sensorRows(paths) {
12013
12491
  });
12014
12492
  }
12015
12493
  async function runnableSensorMemories(paths, regexOnly = true) {
12016
- if (!existsSync42(paths.memoriesDir)) return [];
12017
- const loaded = await loadMemoriesFromDir16(paths.memoriesDir);
12494
+ if (!existsSync43(paths.memoriesDir)) return [];
12495
+ const loaded = await loadMemoriesFromDir15(paths.memoriesDir);
12018
12496
  return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
12019
12497
  const sensor = memory2.frontmatter.sensor;
12020
12498
  if (!sensor) return false;
@@ -12024,7 +12502,7 @@ async function runnableSensorMemories(paths, regexOnly = true) {
12024
12502
  }
12025
12503
  async function stagedDiff(root) {
12026
12504
  try {
12027
- const { stdout } = await exec4("git", ["diff", "--cached"], { cwd: root });
12505
+ const { stdout } = await exec5("git", ["diff", "--cached"], { cwd: root });
12028
12506
  return stdout;
12029
12507
  } catch (err) {
12030
12508
  throw new Error(`git diff --cached failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -12055,15 +12533,15 @@ function shellQuote(value) {
12055
12533
  }
12056
12534
 
12057
12535
  // src/commands/ingest.ts
12058
- import { existsSync as existsSync43 } from "fs";
12536
+ import { existsSync as existsSync44 } from "fs";
12059
12537
  import { mkdir as mkdir18, readFile as readFile21, writeFile as writeFile28 } from "fs/promises";
12060
- import path41 from "path";
12538
+ import path42 from "path";
12061
12539
  import "commander";
12062
12540
  import {
12063
12541
  draftsFromFindings,
12064
12542
  filterNewDrafts,
12065
12543
  findProjectRoot as findProjectRoot40,
12066
- loadMemoriesFromDir as loadMemoriesFromDir17,
12544
+ loadMemoriesFromDir as loadMemoriesFromDir16,
12067
12545
  memoryFilePath as memoryFilePath7,
12068
12546
  parseFindings,
12069
12547
  resolveHaivePaths as resolveHaivePaths37,
@@ -12093,7 +12571,7 @@ function registerIngest(program2) {
12093
12571
  }
12094
12572
  const root = findProjectRoot40(opts.dir);
12095
12573
  const paths = resolveHaivePaths37(root);
12096
- if (!existsSync43(paths.haiveDir)) {
12574
+ if (!existsSync44(paths.haiveDir)) {
12097
12575
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
12098
12576
  process.exitCode = 1;
12099
12577
  return;
@@ -12114,8 +12592,8 @@ function registerIngest(program2) {
12114
12592
  process.exitCode = 1;
12115
12593
  return;
12116
12594
  }
12117
- const reportPath = path41.resolve(root, file);
12118
- if (!existsSync43(reportPath)) {
12595
+ const reportPath = path42.resolve(root, file);
12596
+ if (!existsSync44(reportPath)) {
12119
12597
  ui.error(`Report file not found: ${reportPath}`);
12120
12598
  process.exitCode = 1;
12121
12599
  return;
@@ -12147,7 +12625,7 @@ function registerIngest(program2) {
12147
12625
  process.exitCode = 1;
12148
12626
  return;
12149
12627
  }
12150
- const existing = existsSync43(paths.memoriesDir) ? await loadMemoriesFromDir17(paths.memoriesDir) : [];
12628
+ const existing = existsSync44(paths.memoriesDir) ? await loadMemoriesFromDir16(paths.memoriesDir) : [];
12151
12629
  const existingTopics = new Set(
12152
12630
  existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
12153
12631
  );
@@ -12209,13 +12687,13 @@ function registerIngest(program2) {
12209
12687
  await writeDraft(paths, draft);
12210
12688
  created++;
12211
12689
  }
12212
- ui.success(`Created ${created} proposed memory(ies) under ${path41.relative(root, paths.memoriesDir)}/`);
12690
+ ui.success(`Created ${created} proposed memory(ies) under ${path42.relative(root, paths.memoriesDir)}/`);
12213
12691
  ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
12214
12692
  });
12215
12693
  }
12216
12694
  async function writeDraft(paths, draft) {
12217
12695
  const file = memoryFilePath7(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
12218
- await mkdir18(path41.dirname(file), { recursive: true });
12696
+ await mkdir18(path42.dirname(file), { recursive: true });
12219
12697
  await writeFile28(file, serializeMemory16({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
12220
12698
  return file;
12221
12699
  }
@@ -12257,14 +12735,14 @@ async function fetchSonarIssues(opts) {
12257
12735
  }
12258
12736
 
12259
12737
  // src/commands/dashboard.ts
12260
- import { existsSync as existsSync44 } from "fs";
12738
+ import { existsSync as existsSync45 } from "fs";
12261
12739
  import "commander";
12262
12740
  import {
12263
12741
  buildDashboard,
12264
12742
  findProjectRoot as findProjectRoot41,
12265
12743
  loadConfig as loadConfig14,
12266
- loadMemoriesFromDir as loadMemoriesFromDir18,
12267
- loadPreventionEvents as loadPreventionEvents3,
12744
+ loadMemoriesFromDir as loadMemoriesFromDir17,
12745
+ loadPreventionEvents as loadPreventionEvents4,
12268
12746
  loadUsageIndex as loadUsageIndex16,
12269
12747
  resolveHaivePaths as resolveHaivePaths38
12270
12748
  } from "@hivelore/core";
@@ -12274,14 +12752,14 @@ function registerDashboard(program2) {
12274
12752
  ).option("--json", "emit the full report as JSON", false).option("--top <n>", "rows per top-list", "10").option("--dormant-days <n>", "dormancy window for impact scoring").option("-d, --dir <dir>", "project root").action(async (opts) => {
12275
12753
  const root = findProjectRoot41(opts.dir);
12276
12754
  const paths = resolveHaivePaths38(root);
12277
- if (!existsSync44(paths.haiveDir)) {
12755
+ if (!existsSync45(paths.haiveDir)) {
12278
12756
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
12279
12757
  process.exitCode = 1;
12280
12758
  return;
12281
12759
  }
12282
- const memories = existsSync44(paths.memoriesDir) ? await loadMemoriesFromDir18(paths.memoriesDir) : [];
12760
+ const memories = existsSync45(paths.memoriesDir) ? await loadMemoriesFromDir17(paths.memoriesDir) : [];
12283
12761
  const usage = await loadUsageIndex16(paths);
12284
- const preventionEvents = await loadPreventionEvents3(paths);
12762
+ const preventionEvents = await loadPreventionEvents4(paths);
12285
12763
  const config = await loadConfig14(paths);
12286
12764
  const top = Math.max(1, Number.parseInt(opts.top ?? "10", 10) || 10);
12287
12765
  const dormantDays = opts.dormantDays ? Number.parseInt(opts.dormantDays, 10) : void 0;
@@ -12403,30 +12881,30 @@ function warnNum(n) {
12403
12881
  }
12404
12882
 
12405
12883
  // src/commands/dev-link.ts
12406
- import { execFile as execFile7 } from "child_process";
12884
+ import { execFile as execFile8 } from "child_process";
12407
12885
  import { cp, readFile as readFile22 } from "fs/promises";
12408
- import { existsSync as existsSync45 } from "fs";
12409
- import path42 from "path";
12410
- import { promisify as promisify7 } from "util";
12886
+ import { existsSync as existsSync46 } from "fs";
12887
+ import path43 from "path";
12888
+ import { promisify as promisify8 } from "util";
12411
12889
  import "commander";
12412
12890
  import { findProjectRoot as findProjectRoot42 } from "@hivelore/core";
12413
- var exec5 = promisify7(execFile7);
12891
+ var exec6 = promisify8(execFile8);
12414
12892
  function registerDevLink(program2) {
12415
12893
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
12416
12894
  dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore (or legacy @hiveai) install so the global binary runs your local code.").option("-d, --dir <dir>", "repo root (default: discovered from cwd)").option("--json", "emit a machine-readable summary", false).action(async (opts) => {
12417
12895
  const root = findProjectRoot42(opts.dir);
12418
- if (!existsSync45(path42.join(root, "packages", "cli", "dist", "index.js"))) {
12896
+ if (!existsSync46(path43.join(root, "packages", "cli", "dist", "index.js"))) {
12419
12897
  ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
12420
12898
  process.exitCode = 1;
12421
12899
  return;
12422
12900
  }
12423
12901
  let globalModules;
12424
12902
  try {
12425
- globalModules = (await exec5("npm", ["root", "-g"])).stdout.trim();
12903
+ globalModules = (await exec6("npm", ["root", "-g"])).stdout.trim();
12426
12904
  } catch {
12427
- globalModules = path42.join(path42.dirname(path42.dirname(process.execPath)), "lib", "node_modules");
12905
+ globalModules = path43.join(path43.dirname(path43.dirname(process.execPath)), "lib", "node_modules");
12428
12906
  }
12429
- const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path42.join(globalModules, scope)).filter((dir) => existsSync45(dir));
12907
+ const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path43.join(globalModules, scope)).filter((dir) => existsSync46(dir));
12430
12908
  if (scopeDirs.length === 0) {
12431
12909
  ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
12432
12910
  process.exitCode = 1;
@@ -12434,24 +12912,24 @@ function registerDevLink(program2) {
12434
12912
  }
12435
12913
  const linked = [];
12436
12914
  const copyDist = async (fromPkg, toDistDir) => {
12437
- const from = path42.join(root, "packages", fromPkg, "dist");
12438
- if (!existsSync45(from) || !existsSync45(path42.dirname(toDistDir))) return;
12915
+ const from = path43.join(root, "packages", fromPkg, "dist");
12916
+ if (!existsSync46(from) || !existsSync46(path43.dirname(toDistDir))) return;
12439
12917
  await cp(from, toDistDir, { recursive: true });
12440
- linked.push(path42.relative(globalModules, toDistDir));
12918
+ linked.push(path43.relative(globalModules, toDistDir));
12441
12919
  };
12442
12920
  for (const globalHive of scopeDirs) {
12443
- const nestedScope = path42.basename(globalHive);
12921
+ const nestedScope = path43.basename(globalHive);
12444
12922
  for (const pkg of ["cli", "mcp"]) {
12445
- await copyDist(pkg, path42.join(globalHive, pkg, "dist"));
12923
+ await copyDist(pkg, path43.join(globalHive, pkg, "dist"));
12446
12924
  for (const nested of ["core", "embeddings"]) {
12447
- await copyDist(nested, path42.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
12925
+ await copyDist(nested, path43.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
12448
12926
  }
12449
12927
  }
12450
- await copyDist("core", path42.join(globalHive, "core", "dist"));
12928
+ await copyDist("core", path43.join(globalHive, "core", "dist"));
12451
12929
  }
12452
12930
  let version = "unknown";
12453
12931
  try {
12454
- version = JSON.parse(await readFile22(path42.join(root, "package.json"), "utf8")).version ?? "unknown";
12932
+ version = JSON.parse(await readFile22(path43.join(root, "package.json"), "utf8")).version ?? "unknown";
12455
12933
  } catch {
12456
12934
  }
12457
12935
  if (opts.json) {
@@ -12462,7 +12940,7 @@ function registerDevLink(program2) {
12462
12940
  ui.warn("Nothing linked \u2014 no matching dist targets were found in the global install.");
12463
12941
  return;
12464
12942
  }
12465
- ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path42.basename(d)).join(", ")}`);
12943
+ ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path43.basename(d)).join(", ")}`);
12466
12944
  for (const t of linked) console.log(` ${ui.dim("\u2192")} ${t}`);
12467
12945
  console.log(ui.dim("The global binary now runs your local build (git hooks + MCP included)."));
12468
12946
  });
@@ -12470,8 +12948,8 @@ function registerDevLink(program2) {
12470
12948
 
12471
12949
  // src/commands/coverage.ts
12472
12950
  import { readFile as readFile23 } from "fs/promises";
12473
- import { existsSync as existsSync46 } from "fs";
12474
- import path43 from "path";
12951
+ import { existsSync as existsSync47 } from "fs";
12952
+ import path44 from "path";
12475
12953
  import "commander";
12476
12954
  import {
12477
12955
  findCoverageGaps,
@@ -12481,7 +12959,7 @@ import {
12481
12959
  tallyHotFiles
12482
12960
  } from "@hivelore/core";
12483
12961
  async function readAgentHotFiles(root, cacheFile, sinceMs) {
12484
- if (!existsSync46(cacheFile)) return [];
12962
+ if (!existsSync47(cacheFile)) return [];
12485
12963
  const raw = await readFile23(cacheFile, "utf8").catch(() => "");
12486
12964
  const files = [];
12487
12965
  for (const line of raw.split("\n")) {
@@ -12495,7 +12973,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
12495
12973
  }
12496
12974
  for (const f of obs.files ?? []) {
12497
12975
  if (typeof f !== "string" || !f) continue;
12498
- const rel = path43.isAbsolute(f) ? path43.relative(root, f) : f;
12976
+ const rel = path44.isAbsolute(f) ? path44.relative(root, f) : f;
12499
12977
  if (rel.startsWith("..")) continue;
12500
12978
  files.push(rel);
12501
12979
  }
@@ -12539,7 +13017,7 @@ function registerCoverage(program2) {
12539
13017
  }) : null;
12540
13018
  const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
12541
13019
  const sinceMs = Date.now() - days * 864e5;
12542
- const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path43.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
13020
+ const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path44.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
12543
13021
  const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
12544
13022
  const memories = await loadMemoriesFromDir(paths.memoriesDir);
12545
13023
  const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
@@ -12577,8 +13055,8 @@ function registerCoverage(program2) {
12577
13055
 
12578
13056
  // src/commands/merge-driver.ts
12579
13057
  import { execFileSync as execFileSync3 } from "child_process";
12580
- import { readFileSync, writeFileSync, existsSync as existsSync47 } from "fs";
12581
- import path44 from "path";
13058
+ import { readFileSync, writeFileSync, existsSync as existsSync48 } from "fs";
13059
+ import path45 from "path";
12582
13060
  import "commander";
12583
13061
  import { findProjectRoot as findProjectRoot44, mergeMemoryVersions } from "@hivelore/core";
12584
13062
  var GITATTRIBUTES_MARK = "# Hivelore merge driver";
@@ -12610,8 +13088,8 @@ function registerMergeDriver(program2) {
12610
13088
  process.exitCode = 1;
12611
13089
  return;
12612
13090
  }
12613
- const gaPath = path44.join(root, ".gitattributes");
12614
- let content = existsSync47(gaPath) ? readFileSync(gaPath, "utf8") : "";
13091
+ const gaPath = path45.join(root, ".gitattributes");
13092
+ let content = existsSync48(gaPath) ? readFileSync(gaPath, "utf8") : "";
12615
13093
  if (!content.includes(GITATTRIBUTES_MARK)) {
12616
13094
  if (content.length > 0 && !content.endsWith("\n")) content += "\n";
12617
13095
  content += GITATTRIBUTES_BLOCK + "\n";
@@ -12625,21 +13103,21 @@ function registerMergeDriver(program2) {
12625
13103
  }
12626
13104
 
12627
13105
  // src/commands/bridges.ts
12628
- import { existsSync as existsSync48 } from "fs";
12629
- import path45 from "path";
13106
+ import { existsSync as existsSync49 } from "fs";
13107
+ import path46 from "path";
12630
13108
  import "commander";
12631
13109
  import {
12632
13110
  findProjectRoot as findProjectRoot45,
12633
13111
  resolveHaivePaths as resolveHaivePaths40,
12634
- BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
12635
- BRIDGE_TARGETS as BRIDGE_TARGETS3
13112
+ BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
13113
+ BRIDGE_TARGETS as BRIDGE_TARGETS4
12636
13114
  } from "@hivelore/core";
12637
13115
  function registerBridges(program2) {
12638
13116
  const bridges = program2.command("bridges").description(
12639
13117
  "Generate native agent bridge files from the Hivelore corpus.\n Bridges inject top validated memories and block sensors into agent-harness-specific\n config files (.cursor/rules/haive-memories.mdc, .clinerules, .windsurfrules,\n .continuerules, .sourcegraph/cody-rules.md, .rules, AGENTS.md,\n .github/copilot-instructions.md).\n This is the reach differentiator vs memories.sh: our bridges carry enforcement, not just injection.\n\n Example:\n hivelore bridges sync --all\n hivelore bridges sync --only cline,windsurf\n"
12640
13118
  );
12641
13119
  bridges.command("sync").description(
12642
- "Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS3.join(", ") + "\n"
13120
+ "Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS4.join(", ") + "\n"
12643
13121
  ).option("--all", "generate all supported bridge targets").option(
12644
13122
  "--only <targets>",
12645
13123
  "comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
@@ -12647,7 +13125,7 @@ function registerBridges(program2) {
12647
13125
  const root = findProjectRoot45(opts.dir);
12648
13126
  const paths = resolveHaivePaths40(root);
12649
13127
  const dryRun = opts.dryRun === true;
12650
- if (!existsSync48(paths.memoriesDir)) {
13128
+ if (!existsSync49(paths.memoriesDir)) {
12651
13129
  ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
12652
13130
  process.exitCode = 1;
12653
13131
  return;
@@ -12655,18 +13133,18 @@ function registerBridges(program2) {
12655
13133
  let targets;
12656
13134
  if (opts.only) {
12657
13135
  const requested = opts.only.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
12658
- const invalid = requested.filter((t) => !BRIDGE_TARGETS3.includes(t));
13136
+ const invalid = requested.filter((t) => !BRIDGE_TARGETS4.includes(t));
12659
13137
  if (invalid.length > 0) {
12660
- ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS3.join(", ")}`);
13138
+ ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS4.join(", ")}`);
12661
13139
  process.exitCode = 1;
12662
13140
  return;
12663
13141
  }
12664
13142
  targets = requested;
12665
13143
  } else if (opts.all) {
12666
- targets = BRIDGE_TARGETS3;
13144
+ targets = BRIDGE_TARGETS4;
12667
13145
  } else {
12668
- targets = BRIDGE_TARGETS3.filter(
12669
- (t) => existsSync48(path45.join(root, BRIDGE_TARGET_PATH2[t]))
13146
+ targets = BRIDGE_TARGETS4.filter(
13147
+ (t) => existsSync49(path46.join(root, BRIDGE_TARGET_PATH3[t]))
12670
13148
  );
12671
13149
  if (targets.length === 0) {
12672
13150
  ui.info(
@@ -12698,7 +13176,7 @@ function registerBridges(program2) {
12698
13176
  const root = findProjectRoot45(opts.dir);
12699
13177
  const paths = resolveHaivePaths40(root);
12700
13178
  const statuses = await getBridgeFileStatuses(root, paths, {
12701
- targets: BRIDGE_TARGETS3,
13179
+ targets: BRIDGE_TARGETS4,
12702
13180
  maxMemories: Math.max(1, Number(opts.maxMemories ?? 8))
12703
13181
  });
12704
13182
  console.log(ui.bold("Hivelore bridge targets:"));
@@ -12714,7 +13192,7 @@ function registerBridges(program2) {
12714
13192
 
12715
13193
  // src/index.ts
12716
13194
  var program = new Command48();
12717
- program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.33.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
13195
+ program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.35.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
12718
13196
  registerInit(program);
12719
13197
  registerResolveProject(program);
12720
13198
  registerEnforce(program);