@hiveai/cli 0.18.0 → 0.20.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
@@ -262,6 +262,7 @@ import {
262
262
  loadCodeMap,
263
263
  loadMemoriesFromDir,
264
264
  loadUsageIndex,
265
+ looksLikeGenericAdvice,
265
266
  resolveHaivePaths,
266
267
  serializeMemory,
267
268
  specificityScore
@@ -310,7 +311,9 @@ async function lintMemoriesAsync(root, options = {}) {
310
311
  message: "Record does not contain obvious action/rationale words. Add the concrete rule, why it exists, and what to do instead."
311
312
  });
312
313
  }
313
- if (["decision", "gotcha", "convention", "architecture"].includes(fm.type) && fm.status !== "rejected" && naked.length >= 40 && specificityScore(naked) < 0.2) {
314
+ if (["decision", "gotcha", "convention", "architecture"].includes(fm.type) && fm.status !== "rejected" && naked.length >= 40 && specificityScore(naked) < 0.2 && // High-precision gate: only flag when there is positive evidence of generic advice.
315
+ // A low-density but arbitrary team policy (unguessable prose) must not be flagged.
316
+ looksLikeGenericAdvice(naked)) {
314
317
  out.push({
315
318
  file: filePath,
316
319
  id: fm.id,
@@ -1378,14 +1381,15 @@ async function reportIndexStatus(root, paths, asJson) {
1378
1381
 
1379
1382
  // src/commands/init.ts
1380
1383
  import { execFile as execFile2 } from "child_process";
1381
- import { mkdir as mkdir5, readFile as readFile5, readdir as readdir2, writeFile as writeFile6 } from "fs/promises";
1382
- import { existsSync as existsSync10 } from "fs";
1383
- import path10 from "path";
1384
+ import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
1385
+ import { existsSync as existsSync11 } from "fs";
1386
+ import path11 from "path";
1384
1387
  import { spawnSync as spawnSync3 } from "child_process";
1385
1388
  import { promisify as promisify2 } from "util";
1386
1389
  import "commander";
1387
1390
  import {
1388
1391
  AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
1392
+ BRIDGE_TARGETS,
1389
1393
  buildCodeMap as buildCodeMap3,
1390
1394
  buildFrontmatter as buildFrontmatter2,
1391
1395
  detectStacksFromManifests,
@@ -1397,20 +1401,101 @@ import {
1397
1401
  serializeMemory as serializeMemory3
1398
1402
  } from "@hiveai/core";
1399
1403
 
1404
+ // src/utils/bridge-files.ts
1405
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
1406
+ import { existsSync as existsSync6 } from "fs";
1407
+ import path6 from "path";
1408
+ import {
1409
+ BRIDGE_MARKERS,
1410
+ generateBridges,
1411
+ isRetiredMemory,
1412
+ loadMemoriesFromDir as loadMemoriesFromDir4
1413
+ } from "@hiveai/core";
1414
+ async function writeBridgeFiles(root, paths, opts) {
1415
+ const result = { created: [], updated: [], unchanged: [] };
1416
+ if (!existsSync6(paths.memoriesDir)) return result;
1417
+ const allLoaded = await loadMemoriesFromDir4(paths.memoriesDir);
1418
+ const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory(m.frontmatter, m.body));
1419
+ const sensors = [];
1420
+ for (const m of memories) {
1421
+ const sensor = m.frontmatter.sensor;
1422
+ if (!sensor || sensor.severity !== "block") continue;
1423
+ sensors.push({
1424
+ id: m.frontmatter.id,
1425
+ severity: "block",
1426
+ message: sensor.message,
1427
+ ...sensor.pattern ? { pattern: sensor.pattern } : {},
1428
+ paths: sensor.paths.length > 0 ? sensor.paths : m.frontmatter.anchor.paths
1429
+ });
1430
+ }
1431
+ const maxMemories = Math.max(1, opts.maxMemories ?? 8);
1432
+ const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
1433
+ for (const output of outputs) {
1434
+ const targetFile = path6.join(root, output.path);
1435
+ const fileExists = existsSync6(targetFile);
1436
+ if (opts.onlyExisting && !fileExists) continue;
1437
+ if (opts.dryRun) {
1438
+ (fileExists ? result.updated : result.created).push(output.path);
1439
+ continue;
1440
+ }
1441
+ await mkdir2(path6.dirname(targetFile), { recursive: true });
1442
+ if (!fileExists) {
1443
+ await writeFile3(targetFile, output.content, "utf8");
1444
+ result.created.push(output.path);
1445
+ continue;
1446
+ }
1447
+ let existing = (await readFile3(targetFile, "utf8")).replace(/\r\n/g, "\n");
1448
+ const withMemories = replaceMarkerBlock(
1449
+ existing,
1450
+ BRIDGE_MARKERS.memoriesStart,
1451
+ BRIDGE_MARKERS.memoriesEnd,
1452
+ extractMarkerBlock(output.content, BRIDGE_MARKERS.memoriesStart, BRIDGE_MARKERS.memoriesEnd)
1453
+ );
1454
+ const sensorsBlockContent = extractMarkerBlock(
1455
+ output.content,
1456
+ BRIDGE_MARKERS.sensorsStart,
1457
+ BRIDGE_MARKERS.sensorsEnd
1458
+ );
1459
+ const withSensors = sensorsBlockContent ? replaceMarkerBlock(withMemories, BRIDGE_MARKERS.sensorsStart, BRIDGE_MARKERS.sensorsEnd, sensorsBlockContent) : withMemories;
1460
+ if (withSensors === existing) {
1461
+ result.unchanged.push(output.path);
1462
+ continue;
1463
+ }
1464
+ await writeFile3(targetFile, withSensors, "utf8");
1465
+ result.updated.push(output.path);
1466
+ }
1467
+ return result;
1468
+ }
1469
+ function extractMarkerBlock(text, startMarker, endMarker) {
1470
+ const startIdx = text.indexOf(startMarker);
1471
+ const endIdx = text.indexOf(endMarker);
1472
+ if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;
1473
+ return text.slice(startIdx, endIdx + endMarker.length);
1474
+ }
1475
+ function replaceMarkerBlock(existing, startMarker, endMarker, replacement) {
1476
+ if (!replacement) return existing;
1477
+ const startIdx = existing.indexOf(startMarker);
1478
+ const endIdx = existing.indexOf(endMarker);
1479
+ if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
1480
+ return existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + replacement + "\n";
1481
+ }
1482
+ return existing.slice(0, startIdx) + replacement + existing.slice(endIdx + endMarker.length);
1483
+ }
1484
+
1400
1485
  // src/commands/agent.ts
1401
1486
  import { spawnSync as spawnSync2 } from "child_process";
1402
- import { existsSync as existsSync7 } from "fs";
1403
- import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
1487
+ import { existsSync as existsSync8 } from "fs";
1488
+ import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
1404
1489
  import os2 from "os";
1405
- import path7 from "path";
1490
+ import path8 from "path";
1406
1491
  import { createInterface } from "readline/promises";
1407
1492
  import "commander";
1408
1493
  import { findProjectRoot as findProjectRoot6, resolveHaivePaths as resolveHaivePaths5 } from "@hiveai/core";
1409
1494
 
1410
1495
  // src/commands/init-mcp-setup.ts
1411
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
1412
- import { existsSync as existsSync6 } from "fs";
1413
- import path6 from "path";
1496
+ import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
1497
+ import { existsSync as existsSync7 } from "fs";
1498
+ import path7 from "path";
1414
1499
  import os from "os";
1415
1500
  var HOME = os.homedir();
1416
1501
  var HAIVE_MCP_ENTRY = {
@@ -1425,38 +1510,38 @@ function projectMcpEntry(root) {
1425
1510
  };
1426
1511
  }
1427
1512
  function cursorMcpPath() {
1428
- return path6.join(HOME, ".cursor", "mcp.json");
1513
+ return path7.join(HOME, ".cursor", "mcp.json");
1429
1514
  }
1430
1515
  async function configureCursor() {
1431
1516
  const mcpPath = cursorMcpPath();
1432
- const cursorDir = path6.join(HOME, ".cursor");
1433
- if (!existsSync6(cursorDir)) return { client: "Cursor", status: "not_installed" };
1517
+ const cursorDir = path7.join(HOME, ".cursor");
1518
+ if (!existsSync7(cursorDir)) return { client: "Cursor", status: "not_installed" };
1434
1519
  let config = {};
1435
- if (existsSync6(mcpPath)) {
1520
+ if (existsSync7(mcpPath)) {
1436
1521
  try {
1437
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1522
+ config = JSON.parse(await readFile4(mcpPath, "utf8"));
1438
1523
  } catch {
1439
1524
  }
1440
1525
  }
1441
1526
  config.mcpServers ??= {};
1442
1527
  if (config.mcpServers["haive"]) return { client: "Cursor", status: "already_configured" };
1443
1528
  config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
1444
- await mkdir2(cursorDir, { recursive: true });
1445
- await writeFile3(mcpPath, JSON.stringify(config, null, 2), "utf8");
1529
+ await mkdir3(cursorDir, { recursive: true });
1530
+ await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1446
1531
  return { client: "Cursor", status: "configured", path: mcpPath };
1447
1532
  }
1448
1533
  function vscodeMcpPath() {
1449
1534
  const candidates = [
1450
- path6.join(HOME, ".config", "Code", "User", "mcp.json"),
1535
+ path7.join(HOME, ".config", "Code", "User", "mcp.json"),
1451
1536
  // Linux
1452
- path6.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1537
+ path7.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1453
1538
  // macOS
1454
- path6.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1539
+ path7.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1455
1540
  // Windows
1456
- path6.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1541
+ path7.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1457
1542
  ];
1458
1543
  for (const c of candidates) {
1459
- if (existsSync6(path6.dirname(c))) return c;
1544
+ if (existsSync7(path7.dirname(c))) return c;
1460
1545
  }
1461
1546
  return null;
1462
1547
  }
@@ -1464,51 +1549,51 @@ async function configureVSCode() {
1464
1549
  const mcpPath = vscodeMcpPath();
1465
1550
  if (!mcpPath) return { client: "VS Code", status: "not_installed" };
1466
1551
  let config = {};
1467
- if (existsSync6(mcpPath)) {
1552
+ if (existsSync7(mcpPath)) {
1468
1553
  try {
1469
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1554
+ config = JSON.parse(await readFile4(mcpPath, "utf8"));
1470
1555
  } catch {
1471
1556
  }
1472
1557
  }
1473
1558
  config.servers ??= {};
1474
1559
  if (config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
1475
1560
  config.servers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1476
- await mkdir2(path6.dirname(mcpPath), { recursive: true });
1477
- await writeFile3(mcpPath, JSON.stringify(config, null, 2), "utf8");
1561
+ await mkdir3(path7.dirname(mcpPath), { recursive: true });
1562
+ await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1478
1563
  return { client: "VS Code", status: "configured", path: mcpPath };
1479
1564
  }
1480
1565
  function claudeConfigPath() {
1481
- const p = path6.join(HOME, ".claude.json");
1482
- if (existsSync6(p)) return p;
1483
- const p2 = path6.join(HOME, ".config", "claude", "claude.json");
1484
- if (existsSync6(path6.dirname(p2))) return p2;
1566
+ const p = path7.join(HOME, ".claude.json");
1567
+ if (existsSync7(p)) return p;
1568
+ const p2 = path7.join(HOME, ".config", "claude", "claude.json");
1569
+ if (existsSync7(path7.dirname(p2))) return p2;
1485
1570
  return null;
1486
1571
  }
1487
1572
  async function configureClaude() {
1488
- const cfgPath = claudeConfigPath() ?? path6.join(HOME, ".claude.json");
1489
- if (!existsSync6(cfgPath) && !existsSync6(path6.join(HOME, ".claude"))) {
1573
+ const cfgPath = claudeConfigPath() ?? path7.join(HOME, ".claude.json");
1574
+ if (!existsSync7(cfgPath) && !existsSync7(path7.join(HOME, ".claude"))) {
1490
1575
  return { client: "Claude Code", status: "not_installed" };
1491
1576
  }
1492
1577
  let config = {};
1493
- if (existsSync6(cfgPath)) {
1578
+ if (existsSync7(cfgPath)) {
1494
1579
  try {
1495
- config = JSON.parse(await readFile3(cfgPath, "utf8"));
1580
+ config = JSON.parse(await readFile4(cfgPath, "utf8"));
1496
1581
  } catch {
1497
1582
  }
1498
1583
  }
1499
1584
  config.mcpServers ??= {};
1500
1585
  if (config.mcpServers["haive"]) return { client: "Claude Code", status: "already_configured" };
1501
1586
  config.mcpServers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1502
- await writeFile3(cfgPath, JSON.stringify(config, null, 2), "utf8");
1587
+ await writeFile4(cfgPath, JSON.stringify(config, null, 2), "utf8");
1503
1588
  return { client: "Claude Code", status: "configured", path: cfgPath };
1504
1589
  }
1505
1590
  function windsurfMcpPath() {
1506
1591
  const candidates = [
1507
- path6.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1508
- path6.join(HOME, ".windsurf", "mcp.json")
1592
+ path7.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1593
+ path7.join(HOME, ".windsurf", "mcp.json")
1509
1594
  ];
1510
1595
  for (const c of candidates) {
1511
- if (existsSync6(path6.dirname(c))) return c;
1596
+ if (existsSync7(path7.dirname(c))) return c;
1512
1597
  }
1513
1598
  return null;
1514
1599
  }
@@ -1516,17 +1601,17 @@ async function configureWindsurf() {
1516
1601
  const mcpPath = windsurfMcpPath();
1517
1602
  if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
1518
1603
  let config = {};
1519
- if (existsSync6(mcpPath)) {
1604
+ if (existsSync7(mcpPath)) {
1520
1605
  try {
1521
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1606
+ config = JSON.parse(await readFile4(mcpPath, "utf8"));
1522
1607
  } catch {
1523
1608
  }
1524
1609
  }
1525
1610
  config.mcpServers ??= {};
1526
1611
  if (config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
1527
1612
  config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
1528
- await mkdir2(path6.dirname(mcpPath), { recursive: true });
1529
- await writeFile3(mcpPath, JSON.stringify(config, null, 2), "utf8");
1613
+ await mkdir3(path7.dirname(mcpPath), { recursive: true });
1614
+ await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1530
1615
  return { client: "Windsurf", status: "configured", path: mcpPath };
1531
1616
  }
1532
1617
  async function autoConfigureMcpClients() {
@@ -1546,51 +1631,51 @@ async function configureProjectMcpClients(root) {
1546
1631
  const entry = projectMcpEntry(root);
1547
1632
  const results = [];
1548
1633
  try {
1549
- const cursorPath = path6.join(root, ".cursor", "mcp.json");
1634
+ const cursorPath = path7.join(root, ".cursor", "mcp.json");
1550
1635
  let config = {};
1551
- if (existsSync6(cursorPath)) {
1636
+ if (existsSync7(cursorPath)) {
1552
1637
  try {
1553
- config = JSON.parse(await readFile3(cursorPath, "utf8"));
1638
+ config = JSON.parse(await readFile4(cursorPath, "utf8"));
1554
1639
  } catch {
1555
1640
  }
1556
1641
  }
1557
1642
  config.mcpServers ??= {};
1558
1643
  config.mcpServers["haive"] = entry;
1559
- await mkdir2(path6.dirname(cursorPath), { recursive: true });
1560
- await writeFile3(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1644
+ await mkdir3(path7.dirname(cursorPath), { recursive: true });
1645
+ await writeFile4(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1561
1646
  results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
1562
1647
  } catch (err) {
1563
1648
  results.push({ client: "Cursor (project)", status: "error", error: String(err) });
1564
1649
  }
1565
1650
  try {
1566
- const vscodePath = path6.join(root, ".vscode", "mcp.json");
1651
+ const vscodePath = path7.join(root, ".vscode", "mcp.json");
1567
1652
  let config = {};
1568
- if (existsSync6(vscodePath)) {
1653
+ if (existsSync7(vscodePath)) {
1569
1654
  try {
1570
- config = JSON.parse(await readFile3(vscodePath, "utf8"));
1655
+ config = JSON.parse(await readFile4(vscodePath, "utf8"));
1571
1656
  } catch {
1572
1657
  }
1573
1658
  }
1574
1659
  config.servers ??= {};
1575
1660
  config.servers["haive"] = { ...entry, type: "stdio" };
1576
- await mkdir2(path6.dirname(vscodePath), { recursive: true });
1577
- await writeFile3(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
1661
+ await mkdir3(path7.dirname(vscodePath), { recursive: true });
1662
+ await writeFile4(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
1578
1663
  results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
1579
1664
  } catch (err) {
1580
1665
  results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
1581
1666
  }
1582
1667
  try {
1583
- const mcpPath = path6.join(root, ".mcp.json");
1668
+ const mcpPath = path7.join(root, ".mcp.json");
1584
1669
  let config = {};
1585
- if (existsSync6(mcpPath)) {
1670
+ if (existsSync7(mcpPath)) {
1586
1671
  try {
1587
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1672
+ config = JSON.parse(await readFile4(mcpPath, "utf8"));
1588
1673
  } catch {
1589
1674
  }
1590
1675
  }
1591
1676
  config.mcpServers ??= {};
1592
1677
  config.mcpServers["haive"] = { ...entry, type: "stdio" };
1593
- await writeFile3(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1678
+ await writeFile4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1594
1679
  results.push({ client: "Claude Code (project)", status: "configured", path: mcpPath });
1595
1680
  } catch (err) {
1596
1681
  results.push({ client: "Claude Code (project)", status: "error", error: String(err) });
@@ -1656,9 +1741,9 @@ async function detectAgentMode(dir) {
1656
1741
  const root = findProjectRoot6(dir);
1657
1742
  const paths = resolveHaivePaths5(root);
1658
1743
  const projectMcp = [
1659
- { client: "Claude Code", path: path7.join(root, ".mcp.json"), present: existsSync7(path7.join(root, ".mcp.json")) },
1660
- { client: "Cursor", path: path7.join(root, ".cursor", "mcp.json"), present: existsSync7(path7.join(root, ".cursor", "mcp.json")) },
1661
- { client: "VS Code", path: path7.join(root, ".vscode", "mcp.json"), present: existsSync7(path7.join(root, ".vscode", "mcp.json")) }
1744
+ { client: "Claude Code", path: path8.join(root, ".mcp.json"), present: existsSync8(path8.join(root, ".mcp.json")) },
1745
+ { client: "Cursor", path: path8.join(root, ".cursor", "mcp.json"), present: existsSync8(path8.join(root, ".cursor", "mcp.json")) },
1746
+ { client: "VS Code", path: path8.join(root, ".vscode", "mcp.json"), present: existsSync8(path8.join(root, ".vscode", "mcp.json")) }
1662
1747
  ];
1663
1748
  const installedAgents = [
1664
1749
  { agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
@@ -1673,7 +1758,7 @@ async function detectAgentMode(dir) {
1673
1758
  const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `haive run -- ${wrapperAgent.command}` : 'haive briefing --task "..." --files "..."';
1674
1759
  return {
1675
1760
  root,
1676
- initialized: existsSync7(paths.haiveDir),
1761
+ initialized: existsSync8(paths.haiveDir),
1677
1762
  project_mcp: projectMcp,
1678
1763
  installed_agents: installedAgents,
1679
1764
  recommended_mode: recommendedMode,
@@ -1681,9 +1766,9 @@ async function detectAgentMode(dir) {
1681
1766
  };
1682
1767
  }
1683
1768
  async function writeAgentModeRecord(paths, detection, skippedReason) {
1684
- const dir = path7.join(paths.runtimeDir, "enforcement");
1685
- await mkdir3(dir, { recursive: true });
1686
- const file = path7.join(dir, "agent-mode.json");
1769
+ const dir = path8.join(paths.runtimeDir, "enforcement");
1770
+ await mkdir4(dir, { recursive: true });
1771
+ const file = path8.join(dir, "agent-mode.json");
1687
1772
  const record = {
1688
1773
  selected_mode: detection.recommended_mode,
1689
1774
  recommended_command: detection.recommended_command,
@@ -1696,7 +1781,7 @@ async function writeAgentModeRecord(paths, detection, skippedReason) {
1696
1781
  ...skippedReason ? [skippedReason] : []
1697
1782
  ]
1698
1783
  };
1699
- await writeFile4(file, JSON.stringify(record, null, 2) + "\n", "utf8");
1784
+ await writeFile5(file, JSON.stringify(record, null, 2) + "\n", "utf8");
1700
1785
  return file;
1701
1786
  }
1702
1787
  async function confirmGlobalSetup() {
@@ -1724,7 +1809,7 @@ async function configureCodexIfAvailable(root) {
1724
1809
  "mcp",
1725
1810
  "--stdio"
1726
1811
  ], { encoding: "utf8" });
1727
- if (result.status === 0) return { client: "Codex", status: "configured", path: path7.join(os2.homedir(), ".codex", "config.toml") };
1812
+ if (result.status === 0) return { client: "Codex", status: "configured", path: path8.join(os2.homedir(), ".codex", "config.toml") };
1728
1813
  return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
1729
1814
  }
1730
1815
  function commandExists(command) {
@@ -1751,7 +1836,7 @@ function printDetection(detection, json) {
1751
1836
  console.log(ui.dim(` root: ${detection.root}`));
1752
1837
  console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
1753
1838
  for (const cfg of detection.project_mcp) {
1754
- console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path7.relative(detection.root, cfg.path))}`);
1839
+ console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path8.relative(detection.root, cfg.path))}`);
1755
1840
  }
1756
1841
  for (const agent of detection.installed_agents) {
1757
1842
  const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
@@ -1779,9 +1864,9 @@ function printSetupResult(result) {
1779
1864
  }
1780
1865
 
1781
1866
  // src/commands/init-bootstrap.ts
1782
- import { readdir, readFile as readFile4 } from "fs/promises";
1783
- import { existsSync as existsSync8, readdirSync } from "fs";
1784
- import path8 from "path";
1867
+ import { readdir, readFile as readFile5 } from "fs/promises";
1868
+ import { existsSync as existsSync9, readdirSync } from "fs";
1869
+ import path9 from "path";
1785
1870
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
1786
1871
  "node_modules",
1787
1872
  "dist",
@@ -1867,12 +1952,12 @@ function detectKeyDeps(allDeps) {
1867
1952
  return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
1868
1953
  }
1869
1954
  function detectLanguage(root) {
1870
- if (existsSync8(path8.join(root, "tsconfig.json"))) return "TypeScript";
1871
- if (existsSync8(path8.join(root, "pyproject.toml")) || existsSync8(path8.join(root, "setup.py"))) return "Python";
1872
- if (existsSync8(path8.join(root, "go.mod"))) return "Go";
1873
- if (existsSync8(path8.join(root, "pom.xml")) || existsSync8(path8.join(root, "build.gradle"))) return "Java/Kotlin";
1874
- if (existsSync8(path8.join(root, "Cargo.toml"))) return "Rust";
1875
- if (existsSync8(path8.join(root, "package.json"))) {
1955
+ if (existsSync9(path9.join(root, "tsconfig.json"))) return "TypeScript";
1956
+ if (existsSync9(path9.join(root, "pyproject.toml")) || existsSync9(path9.join(root, "setup.py"))) return "Python";
1957
+ if (existsSync9(path9.join(root, "go.mod"))) return "Go";
1958
+ if (existsSync9(path9.join(root, "pom.xml")) || existsSync9(path9.join(root, "build.gradle"))) return "Java/Kotlin";
1959
+ if (existsSync9(path9.join(root, "Cargo.toml"))) return "Rust";
1960
+ if (existsSync9(path9.join(root, "package.json"))) {
1876
1961
  return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
1877
1962
  }
1878
1963
  return "Unknown";
@@ -1892,7 +1977,7 @@ function hasSourceWithExt(root, exts) {
1892
1977
  if (depth <= 0) return false;
1893
1978
  for (const entry of entries) {
1894
1979
  if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
1895
- if (scanDir(path8.join(dir, entry.name), depth - 1)) return true;
1980
+ if (scanDir(path9.join(dir, entry.name), depth - 1)) return true;
1896
1981
  }
1897
1982
  }
1898
1983
  return false;
@@ -1913,7 +1998,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
1913
1998
  if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
1914
1999
  if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
1915
2000
  if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
1916
- if (existsSync8("pom.xml")) return "Java backend";
2001
+ if (existsSync9("pom.xml")) return "Java backend";
1917
2002
  return "Application";
1918
2003
  }
1919
2004
  async function scanDirs(root, maxDepth = 2) {
@@ -1929,9 +2014,9 @@ async function scanDirs(root, maxDepth = 2) {
1929
2014
  for (const entry of entries) {
1930
2015
  if (!entry.isDirectory()) continue;
1931
2016
  if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
1932
- const rel = path8.relative(root, path8.join(dir, entry.name));
2017
+ const rel = path9.relative(root, path9.join(dir, entry.name));
1933
2018
  results.push(rel);
1934
- await walk(path8.join(dir, entry.name), depth + 1);
2019
+ await walk(path9.join(dir, entry.name), depth + 1);
1935
2020
  }
1936
2021
  }
1937
2022
  await walk(root, 0);
@@ -2048,10 +2133,10 @@ function readmeExcerpt(readme) {
2048
2133
  }
2049
2134
  async function generateBootstrapContext(root) {
2050
2135
  let pkg = {};
2051
- const pkgPath = path8.join(root, "package.json");
2052
- if (existsSync8(pkgPath)) {
2136
+ const pkgPath = path9.join(root, "package.json");
2137
+ if (existsSync9(pkgPath)) {
2053
2138
  try {
2054
- pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
2139
+ pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
2055
2140
  } catch {
2056
2141
  }
2057
2142
  }
@@ -2061,14 +2146,14 @@ async function generateBootstrapContext(root) {
2061
2146
  const language = detectLanguage(root);
2062
2147
  const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
2063
2148
  const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
2064
- const projectName = pkg.name ?? path8.basename(root);
2149
+ const projectName = pkg.name ?? path9.basename(root);
2065
2150
  const projectDesc = pkg.description ?? "";
2066
2151
  let readmeSummary = "";
2067
2152
  for (const name of ["README.md", "readme.md", "README"]) {
2068
- const p = path8.join(root, name);
2069
- if (existsSync8(p)) {
2153
+ const p = path9.join(root, name);
2154
+ if (existsSync9(p)) {
2070
2155
  try {
2071
- const content = await readFile4(p, "utf8");
2156
+ const content = await readFile5(p, "utf8");
2072
2157
  readmeSummary = readmeExcerpt(content);
2073
2158
  break;
2074
2159
  } catch {
@@ -2132,9 +2217,9 @@ async function generateBootstrapContext(root) {
2132
2217
  }
2133
2218
 
2134
2219
  // src/commands/init-stack-packs.ts
2135
- import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
2136
- import { existsSync as existsSync9 } from "fs";
2137
- import path9 from "path";
2220
+ import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2221
+ import { existsSync as existsSync10 } from "fs";
2222
+ import path10 from "path";
2138
2223
  import {
2139
2224
  buildFrontmatter,
2140
2225
  memoryFilePath,
@@ -3213,7 +3298,7 @@ function autoDetectStacks(deps) {
3213
3298
  async function seedStackPack(haivePaths, stack) {
3214
3299
  const memories = PACKS[stack];
3215
3300
  if (!memories) return { memories: 0, sensors: 0 };
3216
- await mkdir4(haivePaths.teamDir, { recursive: true });
3301
+ await mkdir5(haivePaths.teamDir, { recursive: true });
3217
3302
  let memCount = 0;
3218
3303
  let sensorCount = 0;
3219
3304
  for (const mem of memories) {
@@ -3227,9 +3312,10 @@ async function seedStackPack(haivePaths, stack) {
3227
3312
  autogen: false,
3228
3313
  last_fired: null
3229
3314
  } : void 0;
3315
+ const combinedSlug = mem.slug === stack || mem.slug.startsWith(`${stack}-`) ? mem.slug : `${stack}-${mem.slug}`;
3230
3316
  const fm = buildFrontmatter({
3231
3317
  type: mem.type,
3232
- slug: `${stack}-${mem.slug}`,
3318
+ slug: combinedSlug,
3233
3319
  scope: "team",
3234
3320
  status: "validated",
3235
3321
  // STACK_PACK_TAG marks this as generic seed knowledge so briefing ranking
@@ -3238,12 +3324,12 @@ async function seedStackPack(haivePaths, stack) {
3238
3324
  ...sensor ? { sensor } : {}
3239
3325
  });
3240
3326
  const filePath = memoryFilePath(haivePaths, "team", fm.id);
3241
- if (existsSync9(filePath)) continue;
3327
+ if (existsSync10(filePath)) continue;
3242
3328
  const content = serializeMemory2({ frontmatter: fm, body: `${mem.body}
3243
3329
 
3244
3330
  ${SEED_FOOTER(stack)}` });
3245
- await mkdir4(path9.dirname(filePath), { recursive: true });
3246
- await writeFile5(filePath, content, "utf8");
3331
+ await mkdir5(path10.dirname(filePath), { recursive: true });
3332
+ await writeFile6(filePath, content, "utf8");
3247
3333
  memCount++;
3248
3334
  if (sensor) sensorCount++;
3249
3335
  }
@@ -3252,7 +3338,7 @@ ${SEED_FOOTER(stack)}` });
3252
3338
 
3253
3339
  // src/commands/init.ts
3254
3340
  var execFileAsync = promisify2(execFile2);
3255
- var HAIVE_GITHUB_ACTION_REF = `v${"0.18.0"}`;
3341
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.20.0"}`;
3256
3342
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3257
3343
 
3258
3344
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3274,28 +3360,6 @@ TODO \u2014 domain terms and what they mean here.
3274
3360
  ## Gotchas
3275
3361
  TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.
3276
3362
  `;
3277
- var BRIDGE_BODY = `<!-- hAIve bridge file \u2014 do not edit by hand. -->
3278
-
3279
- This repo uses **hAIve** for shared context. The map:
3280
-
3281
- - \`.ai/project-context.md\` \u2014 project overview, architecture, conventions.
3282
- - \`.ai/memories/\` \u2014 decisions, gotchas, conventions, failed attempts (personal/team/module).
3283
- - The breadcrumbs injected below (if any) are the top current memories.
3284
-
3285
- ## Working through hAIve
3286
-
3287
- 1. **Before editing** for a goal, call \`get_briefing\` (task + files/symbols) to load ranked context \u2014 or \`mem_relevant_to\` if project context is already loaded this session.
3288
- 2. **When an approach fails**, call \`mem_tried\` right away so the next agent skips the dead end.
3289
- 3. **Before closing** a substantive session, run the \`post_task\` prompt to capture what was learned.
3290
- 4. **Before final response**, run \`haive enforce finish\`. If it blocks, commit/push, bump/tag shippable releases, wait for GitHub Actions to pass when applicable, then rerun it.
3291
-
3292
- If the haive MCP server is not available, tell the developer rather than silently skipping it.
3293
-
3294
- ## Safety
3295
-
3296
- - If \`get_briefing\` returns \`action_required\`, surface each item to the developer (use its \`developer_message\`) and wait for confirmation before changing code.
3297
- - Never act autonomously on a cross-repo breaking change (dep bump, contract/API diff) \u2014 ask first.
3298
- `;
3299
3363
  var CURSOR_HAIVE_RULE_MDC = `---
3300
3364
  description: Require hAIve MCP (get_briefing / mem_relevant_to) before substantive repo edits
3301
3365
  alwaysApply: true
@@ -3465,7 +3529,12 @@ jobs:
3465
3529
  function registerInit(program2) {
3466
3530
  program2.command("init").description(
3467
3531
  "Initialize a hAIve 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."
3468
- ).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate CLAUDE.md / AGENTS.md / .cursorrules / copilot-instructions.md / .cursor/rules/haive-mcp-required.mdc").option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
3532
+ ).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate any native agent bridge files").option(
3533
+ "--bridge-targets <list>",
3534
+ `which agent bridges to generate: 'all' (default) | comma-list.
3535
+ Available: ${BRIDGE_TARGETS.join(", ")}. Each carries top memories + block sensors.`,
3536
+ "all"
3537
+ ).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
3469
3538
  "--manual",
3470
3539
  "opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
3471
3540
  ).option(
@@ -3497,7 +3566,7 @@ function registerInit(program2) {
3497
3566
  "approve user-level AI client configuration prompts during agent setup",
3498
3567
  false
3499
3568
  ).option("--json", "emit a machine-readable summary on stdout (human logs go to stderr)", false).action(async (opts) => {
3500
- const root = path10.resolve(opts.dir);
3569
+ const root = path11.resolve(opts.dir);
3501
3570
  const paths = resolveHaivePaths6(root);
3502
3571
  const autopilot = opts.manual !== true;
3503
3572
  const json = opts.json === true;
@@ -3513,35 +3582,36 @@ function registerInit(program2) {
3513
3582
  gitSeedsWritten: 0,
3514
3583
  gitCommitsScanned: 0,
3515
3584
  gitRevertsFound: 0,
3516
- gitRecurring: 0
3585
+ gitRecurring: 0,
3586
+ bridgesWritten: 0
3517
3587
  };
3518
- if (existsSync10(paths.haiveDir)) {
3588
+ if (existsSync11(paths.haiveDir)) {
3519
3589
  ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
3520
3590
  }
3521
- await mkdir5(paths.personalDir, { recursive: true });
3522
- await mkdir5(paths.teamDir, { recursive: true });
3523
- await mkdir5(paths.moduleDir, { recursive: true });
3524
- await mkdir5(paths.modulesContextDir, { recursive: true });
3591
+ await mkdir6(paths.personalDir, { recursive: true });
3592
+ await mkdir6(paths.teamDir, { recursive: true });
3593
+ await mkdir6(paths.moduleDir, { recursive: true });
3594
+ await mkdir6(paths.modulesContextDir, { recursive: true });
3525
3595
  await ensureAiRuntimeLayout(paths.runtimeDir);
3526
- await ensureAiCacheLayout(path10.join(paths.haiveDir, ".cache"));
3527
- if (!existsSync10(paths.projectContext)) {
3596
+ await ensureAiCacheLayout(path11.join(paths.haiveDir, ".cache"));
3597
+ if (!existsSync11(paths.projectContext)) {
3528
3598
  if (wantBootstrap) {
3529
3599
  ui.info("Bootstrapping project context from local files\u2026");
3530
3600
  try {
3531
3601
  const context = await generateBootstrapContext(root);
3532
- await writeFile6(paths.projectContext, context, "utf8");
3602
+ await writeFile7(paths.projectContext, context, "utf8");
3533
3603
  ui.success("Created .ai/project-context.md (auto-bootstrapped from local files)");
3534
3604
  } catch (err) {
3535
3605
  ui.warn(`Bootstrap failed (${String(err)}) \u2014 writing default template instead`);
3536
- await writeFile6(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3606
+ await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3537
3607
  }
3538
3608
  } else {
3539
- await writeFile6(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3540
- ui.success(`Created ${path10.relative(root, paths.projectContext)}`);
3609
+ await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3610
+ ui.success(`Created ${path11.relative(root, paths.projectContext)}`);
3541
3611
  }
3542
3612
  }
3543
- const configExists = existsSync10(
3544
- path10.join(paths.haiveDir, "haive.config.json")
3613
+ const configExists = existsSync11(
3614
+ path11.join(paths.haiveDir, "haive.config.json")
3545
3615
  );
3546
3616
  if (!configExists) {
3547
3617
  await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
@@ -3549,13 +3619,6 @@ function registerInit(program2) {
3549
3619
  `Created .ai/haive.config.json (mode: ${autopilot ? "autopilot" : "standard"})`
3550
3620
  );
3551
3621
  }
3552
- if (opts.bridges) {
3553
- await writeBridge(root, "CLAUDE.md");
3554
- await writeBridge(root, "AGENTS.md");
3555
- await writeBridge(root, ".cursorrules");
3556
- await writeBridge(root, path10.join(".github", "copilot-instructions.md"));
3557
- await writeCursorHaiveRule(root);
3558
- }
3559
3622
  const stacksToSeed = await resolveStacksToSeed(root, wantStack);
3560
3623
  if (stacksToSeed.length > 0) {
3561
3624
  let totalSeeded = 0;
@@ -3597,15 +3660,31 @@ function registerInit(program2) {
3597
3660
  ui.info("Git seeding: no revert/hotfix signals found \u2014 run `haive memory seed-git` later.");
3598
3661
  }
3599
3662
  }
3663
+ if (opts.bridges) {
3664
+ const targets = resolveBridgeTargets(opts.bridgeTargets);
3665
+ const res = await writeBridgeFiles(root, paths, { targets });
3666
+ await writeCursorHaiveRule(root);
3667
+ const made = res.created.length + res.updated.length;
3668
+ report.bridgesWritten = made;
3669
+ if (res.created.length > 0) {
3670
+ ui.success(`Generated ${res.created.length} agent bridge(s): ${res.created.join(", ")}`);
3671
+ }
3672
+ if (res.updated.length > 0) {
3673
+ ui.info(`Refreshed ${res.updated.length} existing bridge(s): ${res.updated.join(", ")}`);
3674
+ }
3675
+ if (made === 0) {
3676
+ ui.info("Bridges already up to date.");
3677
+ }
3678
+ }
3600
3679
  const wantCi = opts.withCi || autopilot;
3601
3680
  if (wantCi) {
3602
- const ciPath = path10.join(root, ".github", "workflows", "haive-sync.yml");
3603
- if (existsSync10(ciPath)) {
3681
+ const ciPath = path11.join(root, ".github", "workflows", "haive-sync.yml");
3682
+ if (existsSync11(ciPath)) {
3604
3683
  ui.info("CI workflow already exists \u2014 skipped");
3605
3684
  } else {
3606
- await mkdir5(path10.dirname(ciPath), { recursive: true });
3607
- await writeFile6(ciPath, CI_WORKFLOW, "utf8");
3608
- ui.success(`Created ${path10.relative(root, ciPath)}`);
3685
+ await mkdir6(path11.dirname(ciPath), { recursive: true });
3686
+ await writeFile7(ciPath, CI_WORKFLOW, "utf8");
3687
+ ui.success(`Created ${path11.relative(root, ciPath)}`);
3609
3688
  }
3610
3689
  }
3611
3690
  if (autopilot) {
@@ -3645,7 +3724,7 @@ function registerInit(program2) {
3645
3724
  interactive: process.stdin.isTTY
3646
3725
  });
3647
3726
  for (const r of agentSetup.project_results) {
3648
- if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path10.relative(root, r.path)})`);
3727
+ if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path11.relative(root, r.path)})`);
3649
3728
  else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
3650
3729
  }
3651
3730
  for (const r of agentSetup.global_results) {
@@ -3682,6 +3761,7 @@ function registerInit(program2) {
3682
3761
  git_seeds_written: report.gitSeedsWritten,
3683
3762
  git_recurring: report.gitRecurring,
3684
3763
  bridges: opts.bridges !== false,
3764
+ bridges_written: report.bridgesWritten,
3685
3765
  ci: opts.withCi || autopilot
3686
3766
  }, null, 2));
3687
3767
  return;
@@ -3743,57 +3823,57 @@ async function autoDetectStacksFromRoot(root) {
3743
3823
  let requirementsTxt;
3744
3824
  let goMod;
3745
3825
  let pomXml;
3746
- const pkgPath = path10.join(root, "package.json");
3747
- if (existsSync10(pkgPath)) {
3826
+ const pkgPath = path11.join(root, "package.json");
3827
+ if (existsSync11(pkgPath)) {
3748
3828
  try {
3749
- const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
3829
+ const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
3750
3830
  packageJsonDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
3751
3831
  } catch {
3752
3832
  }
3753
3833
  }
3754
3834
  for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
3755
- const reqPath = path10.join(root, name);
3756
- if (existsSync10(reqPath)) {
3835
+ const reqPath = path11.join(root, name);
3836
+ if (existsSync11(reqPath)) {
3757
3837
  try {
3758
- requirementsTxt = await readFile5(reqPath, "utf8");
3838
+ requirementsTxt = await readFile6(reqPath, "utf8");
3759
3839
  break;
3760
3840
  } catch {
3761
3841
  }
3762
3842
  }
3763
3843
  }
3764
- const goModPath = path10.join(root, "go.mod");
3765
- if (existsSync10(goModPath)) {
3844
+ const goModPath = path11.join(root, "go.mod");
3845
+ if (existsSync11(goModPath)) {
3766
3846
  try {
3767
- goMod = await readFile5(goModPath, "utf8");
3847
+ goMod = await readFile6(goModPath, "utf8");
3768
3848
  } catch {
3769
3849
  }
3770
3850
  }
3771
- const pomPath = path10.join(root, "pom.xml");
3772
- if (existsSync10(pomPath)) {
3851
+ const pomPath = path11.join(root, "pom.xml");
3852
+ if (existsSync11(pomPath)) {
3773
3853
  try {
3774
- pomXml = await readFile5(pomPath, "utf8");
3854
+ pomXml = await readFile6(pomPath, "utf8");
3775
3855
  } catch {
3776
3856
  }
3777
3857
  }
3778
3858
  let composerJson;
3779
- const composerPath = path10.join(root, "composer.json");
3780
- if (existsSync10(composerPath)) {
3859
+ const composerPath = path11.join(root, "composer.json");
3860
+ if (existsSync11(composerPath)) {
3781
3861
  try {
3782
- composerJson = await readFile5(composerPath, "utf8");
3862
+ composerJson = await readFile6(composerPath, "utf8");
3783
3863
  } catch {
3784
3864
  }
3785
3865
  }
3786
3866
  let gemfile;
3787
- const gemfilePath = path10.join(root, "Gemfile");
3788
- if (existsSync10(gemfilePath)) {
3867
+ const gemfilePath = path11.join(root, "Gemfile");
3868
+ if (existsSync11(gemfilePath)) {
3789
3869
  try {
3790
- gemfile = await readFile5(gemfilePath, "utf8");
3870
+ gemfile = await readFile6(gemfilePath, "utf8");
3791
3871
  } catch {
3792
3872
  }
3793
3873
  }
3794
- const hasDockerfile = existsSync10(path10.join(root, "Dockerfile"));
3795
- const hasTurboJson = existsSync10(path10.join(root, "turbo.json"));
3796
- const hasNxJson = existsSync10(path10.join(root, "nx.json"));
3874
+ const hasDockerfile = existsSync11(path11.join(root, "Dockerfile"));
3875
+ const hasTurboJson = existsSync11(path11.join(root, "turbo.json"));
3876
+ const hasNxJson = existsSync11(path11.join(root, "nx.json"));
3797
3877
  let hasCsproj = false;
3798
3878
  try {
3799
3879
  const entries = await readdir2(root);
@@ -3845,9 +3925,9 @@ async function seedFromGitHistory(root, paths, limit) {
3845
3925
  _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
3846
3926
  `;
3847
3927
  const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
3848
- if (existsSync10(file)) continue;
3849
- await mkdir5(path10.dirname(file), { recursive: true });
3850
- await writeFile6(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
3928
+ if (existsSync11(file)) continue;
3929
+ await mkdir6(path11.dirname(file), { recursive: true });
3930
+ await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
3851
3931
  written++;
3852
3932
  }
3853
3933
  return { scanned: commits.length, found: proposals.length, recurring, written };
@@ -3886,6 +3966,9 @@ function printInitReport(r) {
3886
3966
  if (r.totalMemories > 0) {
3887
3967
  lines.push(` Total ready : ${r.totalMemories} lesson(s), ${r.totalSensors} sensor(s) \u2014 0 written by hand`);
3888
3968
  }
3969
+ if (r.bridgesWritten > 0) {
3970
+ lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (Cursor, Cline, Copilot, Roo, Gemini, \u2026)`);
3971
+ }
3889
3972
  if (lines.length === 0) return;
3890
3973
  const width = Math.max(...lines.map((l) => l.length), 44);
3891
3974
  const bar = "\u2500".repeat(width + 2);
@@ -3900,29 +3983,30 @@ function printInitReport(r) {
3900
3983
  console.log(ui.dim(" Review draft seeds: haive memory pending (validate or delete each one)"));
3901
3984
  }
3902
3985
  console.log(
3903
- ui.dim(" Reach: every agent gets this corpus \u2014 run `haive bridges sync --all` (Cursor, Cline, Windsurf, Roo, Gemini, Copilot, \u2026)")
3986
+ ui.dim(" Reach: bridges auto-refresh on `haive sync`; regenerate any time with `haive bridges sync --all`.")
3904
3987
  );
3905
3988
  }
3906
3989
  async function writeCursorHaiveRule(root) {
3907
3990
  const relPath = ".cursor/rules/haive-mcp-required.mdc";
3908
- const target = path10.join(root, relPath);
3909
- if (existsSync10(target)) {
3991
+ const target = path11.join(root, relPath);
3992
+ if (existsSync11(target)) {
3910
3993
  ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
3911
3994
  return;
3912
3995
  }
3913
- await mkdir5(path10.dirname(target), { recursive: true });
3914
- await writeFile6(target, CURSOR_HAIVE_RULE_MDC, "utf8");
3996
+ await mkdir6(path11.dirname(target), { recursive: true });
3997
+ await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
3915
3998
  ui.success(`Created Cursor rule ${relPath}`);
3916
3999
  }
3917
- async function writeBridge(root, relPath) {
3918
- const target = path10.join(root, relPath);
3919
- if (existsSync10(target)) {
3920
- ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
3921
- return;
4000
+ function resolveBridgeTargets(opt) {
4001
+ const raw = (opt ?? "all").trim().toLowerCase();
4002
+ if (raw === "" || raw === "all") return [...BRIDGE_TARGETS];
4003
+ const requested = raw.split(",").map((t) => t.trim()).filter(Boolean);
4004
+ const valid = requested.filter((t) => BRIDGE_TARGETS.includes(t));
4005
+ const invalid = requested.filter((t) => !BRIDGE_TARGETS.includes(t));
4006
+ if (invalid.length > 0) {
4007
+ ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS.join(", ")}`);
3922
4008
  }
3923
- await mkdir5(path10.dirname(target), { recursive: true });
3924
- await writeFile6(target, BRIDGE_BODY, "utf8");
3925
- ui.success(`Created bridge ${relPath}`);
4009
+ return valid.length > 0 ? valid : [...BRIDGE_TARGETS];
3926
4010
  }
3927
4011
  var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
3928
4012
 
@@ -3937,50 +4021,50 @@ var RUNTIME_GITIGNORE_BODY = `*
3937
4021
  !README.md
3938
4022
  `;
3939
4023
  async function ensureAiRuntimeLayout(runtimeDir) {
3940
- await mkdir5(runtimeDir, { recursive: true });
3941
- const gi = path10.join(runtimeDir, ".gitignore");
3942
- if (!existsSync10(gi)) {
3943
- await writeFile6(gi, RUNTIME_GITIGNORE_BODY, "utf8");
4024
+ await mkdir6(runtimeDir, { recursive: true });
4025
+ const gi = path11.join(runtimeDir, ".gitignore");
4026
+ if (!existsSync11(gi)) {
4027
+ await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
3944
4028
  }
3945
- const readme = path10.join(runtimeDir, "README.md");
3946
- if (!existsSync10(readme)) {
3947
- await writeFile6(readme, RUNTIME_README_BODY, "utf8");
4029
+ const readme = path11.join(runtimeDir, "README.md");
4030
+ if (!existsSync11(readme)) {
4031
+ await writeFile7(readme, RUNTIME_README_BODY, "utf8");
3948
4032
  }
3949
4033
  }
3950
4034
  async function ensureAiCacheLayout(cacheDir) {
3951
- await mkdir5(cacheDir, { recursive: true });
3952
- const gi = path10.join(cacheDir, ".gitignore");
3953
- if (!existsSync10(gi)) {
3954
- await writeFile6(gi, "*\n!.gitignore\n", "utf8");
4035
+ await mkdir6(cacheDir, { recursive: true });
4036
+ const gi = path11.join(cacheDir, ".gitignore");
4037
+ if (!existsSync11(gi)) {
4038
+ await writeFile7(gi, "*\n!.gitignore\n", "utf8");
3955
4039
  }
3956
4040
  }
3957
4041
  async function ensureGitignoreEntries(root, patterns) {
3958
4042
  try {
3959
- const gitignorePath = path10.join(root, ".gitignore");
4043
+ const gitignorePath = path11.join(root, ".gitignore");
3960
4044
  let existing = "";
3961
- if (existsSync10(gitignorePath)) {
3962
- existing = await readFile5(gitignorePath, "utf8");
4045
+ if (existsSync11(gitignorePath)) {
4046
+ existing = await readFile6(gitignorePath, "utf8");
3963
4047
  }
3964
4048
  const lines = existing.split("\n");
3965
4049
  const missing = patterns.filter((p) => !lines.some((l) => l.trim() === p));
3966
4050
  if (missing.length === 0) return;
3967
4051
  const toAppend = (existing.endsWith("\n") || existing === "" ? "" : "\n") + "# hAIve project-level MCP configs (machine-specific absolute paths)\n" + missing.join("\n") + "\n";
3968
- await writeFile6(gitignorePath, existing + toAppend, "utf8");
4052
+ await writeFile7(gitignorePath, existing + toAppend, "utf8");
3969
4053
  } catch {
3970
4054
  }
3971
4055
  }
3972
4056
 
3973
4057
  // src/commands/install-hooks.ts
3974
- import { mkdir as mkdir7, writeFile as writeFile8, chmod, readFile as readFile7 } from "fs/promises";
3975
- import { existsSync as existsSync12 } from "fs";
3976
- import path12 from "path";
4058
+ import { mkdir as mkdir8, writeFile as writeFile9, chmod, readFile as readFile8 } from "fs/promises";
4059
+ import { existsSync as existsSync13 } from "fs";
4060
+ import path13 from "path";
3977
4061
  import "commander";
3978
4062
  import { findProjectRoot as findProjectRoot8 } from "@hiveai/core";
3979
4063
 
3980
4064
  // src/utils/claude-hooks.ts
3981
- import { existsSync as existsSync11 } from "fs";
3982
- import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile7 } from "fs/promises";
3983
- import path11 from "path";
4065
+ import { existsSync as existsSync12 } from "fs";
4066
+ import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
4067
+ import path12 from "path";
3984
4068
  var HAIVE_HOOK_TAG = "haive-enforcement";
3985
4069
  var POST_TOOL_USE_GROUP = {
3986
4070
  matcher: "Edit|Write|Bash",
@@ -4066,9 +4150,9 @@ function unpatchClaudeSettings(input) {
4066
4150
  async function installClaudeHooksAtPath(settingsPath) {
4067
4151
  let raw = null;
4068
4152
  let created = false;
4069
- if (existsSync11(settingsPath)) {
4153
+ if (existsSync12(settingsPath)) {
4070
4154
  try {
4071
- raw = JSON.parse(await readFile6(settingsPath, "utf8"));
4155
+ raw = JSON.parse(await readFile7(settingsPath, "utf8"));
4072
4156
  } catch {
4073
4157
  throw new Error(`${settingsPath} exists but is not valid JSON. Fix it manually first.`);
4074
4158
  }
@@ -4076,25 +4160,25 @@ async function installClaudeHooksAtPath(settingsPath) {
4076
4160
  created = true;
4077
4161
  }
4078
4162
  const patched = patchClaudeSettings(raw);
4079
- await mkdir6(path11.dirname(settingsPath), { recursive: true });
4080
- await writeFile7(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
4163
+ await mkdir7(path12.dirname(settingsPath), { recursive: true });
4164
+ await writeFile8(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
4081
4165
  return { settingsPath, created };
4082
4166
  }
4083
4167
  async function uninstallClaudeHooksAtPath(settingsPath) {
4084
- if (!existsSync11(settingsPath)) {
4168
+ if (!existsSync12(settingsPath)) {
4085
4169
  return { settingsPath, created: false };
4086
4170
  }
4087
- const raw = JSON.parse(await readFile6(settingsPath, "utf8"));
4171
+ const raw = JSON.parse(await readFile7(settingsPath, "utf8"));
4088
4172
  const cleaned = unpatchClaudeSettings(raw);
4089
- await writeFile7(settingsPath, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
4173
+ await writeFile8(settingsPath, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
4090
4174
  return { settingsPath, created: false };
4091
4175
  }
4092
4176
  function defaultClaudeSettingsPath(scope, projectRoot) {
4093
4177
  if (scope === "user") {
4094
4178
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4095
- return path11.join(home, ".claude", "settings.json");
4179
+ return path12.join(home, ".claude", "settings.json");
4096
4180
  }
4097
- return path11.join(projectRoot, ".claude", "settings.local.json");
4181
+ return path12.join(projectRoot, ".claude", "settings.local.json");
4098
4182
  }
4099
4183
 
4100
4184
  // src/commands/install-hooks.ts
@@ -4151,27 +4235,27 @@ fi
4151
4235
  ];
4152
4236
  async function installGitHooks(opts) {
4153
4237
  const root = findProjectRoot8(opts.dir);
4154
- const gitDir = path12.join(root, ".git");
4155
- if (!existsSync12(gitDir)) {
4238
+ const gitDir = path13.join(root, ".git");
4239
+ if (!existsSync13(gitDir)) {
4156
4240
  ui.error(`No .git directory at ${root}.`);
4157
4241
  process.exitCode = 1;
4158
4242
  return;
4159
4243
  }
4160
- const hooksDir = path12.join(gitDir, "hooks");
4161
- await mkdir7(hooksDir, { recursive: true });
4244
+ const hooksDir = path13.join(gitDir, "hooks");
4245
+ await mkdir8(hooksDir, { recursive: true });
4162
4246
  let installed = 0;
4163
4247
  let skipped = 0;
4164
4248
  for (const { name, body } of HOOKS) {
4165
- const file = path12.join(hooksDir, name);
4166
- if (existsSync12(file) && !opts.force) {
4167
- const existing = await readFile7(file, "utf8");
4249
+ const file = path13.join(hooksDir, name);
4250
+ if (existsSync13(file) && !opts.force) {
4251
+ const existing = await readFile8(file, "utf8");
4168
4252
  if (!existing.includes(HOOK_MARKER)) {
4169
4253
  ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
4170
4254
  skipped++;
4171
4255
  continue;
4172
4256
  }
4173
4257
  }
4174
- await writeFile8(file, body, "utf8");
4258
+ await writeFile9(file, body, "utf8");
4175
4259
  await chmod(file, 493);
4176
4260
  installed++;
4177
4261
  }
@@ -4226,9 +4310,9 @@ function registerInstallHooks(program2) {
4226
4310
  }
4227
4311
 
4228
4312
  // src/commands/observe.ts
4229
- import { appendFile, mkdir as mkdir8 } from "fs/promises";
4230
- import { existsSync as existsSync13 } from "fs";
4231
- import path13 from "path";
4313
+ import { appendFile, mkdir as mkdir9 } from "fs/promises";
4314
+ import { existsSync as existsSync14 } from "fs";
4315
+ import path14 from "path";
4232
4316
  import "commander";
4233
4317
  import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
4234
4318
  var MAX_STDIN_BYTES = 256 * 1024;
@@ -4335,7 +4419,7 @@ function registerObserve(program2) {
4335
4419
  })();
4336
4420
  if (!root) return;
4337
4421
  const paths = resolveHaivePaths7(root);
4338
- if (!existsSync13(paths.haiveDir)) return;
4422
+ if (!existsSync14(paths.haiveDir)) return;
4339
4423
  const failureHint = detectFailure(payload);
4340
4424
  const observation = {
4341
4425
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4346,10 +4430,10 @@ function registerObserve(program2) {
4346
4430
  files: extractFiles(payload),
4347
4431
  ...failureHint ? { failure_hint: true } : {}
4348
4432
  };
4349
- const cacheDir = path13.join(paths.haiveDir, ".cache");
4350
- await mkdir8(cacheDir, { recursive: true });
4433
+ const cacheDir = path14.join(paths.haiveDir, ".cache");
4434
+ await mkdir9(cacheDir, { recursive: true });
4351
4435
  await appendFile(
4352
- path13.join(cacheDir, "observations.jsonl"),
4436
+ path14.join(cacheDir, "observations.jsonl"),
4353
4437
  JSON.stringify(observation) + "\n",
4354
4438
  "utf8"
4355
4439
  );
@@ -4366,16 +4450,16 @@ import { findProjectRoot as findProjectRoot11 } from "@hiveai/core";
4366
4450
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4367
4451
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4368
4452
  import { findProjectRoot as findProjectRoot10, resolveHaivePaths as resolveHaivePaths8 } from "@hiveai/core";
4369
- import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
4370
- import { existsSync as existsSync14 } from "fs";
4371
- import path14 from "path";
4453
+ import { mkdir as mkdir10, writeFile as writeFile10 } from "fs/promises";
4454
+ import { existsSync as existsSync15 } from "fs";
4455
+ import path15 from "path";
4372
4456
  import { z } from "zod";
4373
- import { readFile as readFile8, readdir as readdir3 } from "fs/promises";
4457
+ import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
4374
4458
  import { existsSync as existsSync22 } from "fs";
4375
4459
  import path22 from "path";
4376
4460
  import { z as z2 } from "zod";
4377
4461
  import { existsSync as existsSync32 } from "fs";
4378
- import { loadMemoriesFromDir as loadMemoriesFromDir4 } from "@hiveai/core";
4462
+ import { loadMemoriesFromDir as loadMemoriesFromDir5 } from "@hiveai/core";
4379
4463
  import { z as z3 } from "zod";
4380
4464
  import { createHash } from "crypto";
4381
4465
  import { mkdir as mkdir22, writeFile as writeFile22 } from "fs/promises";
@@ -4416,7 +4500,7 @@ import { z as z6 } from "zod";
4416
4500
  import { writeFile as writeFile42 } from "fs/promises";
4417
4501
  import { existsSync as existsSync72 } from "fs";
4418
4502
  import {
4419
- loadMemoriesFromDir as loadMemoriesFromDir5,
4503
+ loadMemoriesFromDir as loadMemoriesFromDir52,
4420
4504
  loadUsageIndex as loadUsageIndex22,
4421
4505
  recordRejection,
4422
4506
  saveUsageIndex,
@@ -4486,7 +4570,7 @@ import {
4486
4570
  } from "@hiveai/core";
4487
4571
  import { z as z14 } from "zod";
4488
4572
  import { mkdir as mkdir32, writeFile as writeFile82 } from "fs/promises";
4489
- import { existsSync as existsSync15 } from "fs";
4573
+ import { existsSync as existsSync152 } from "fs";
4490
4574
  import path52 from "path";
4491
4575
  import {
4492
4576
  buildFrontmatter as buildFrontmatter22,
@@ -4507,7 +4591,7 @@ import {
4507
4591
  serializeMemory as serializeMemory8
4508
4592
  } from "@hiveai/core";
4509
4593
  import { z as z16 } from "zod";
4510
- import { mkdir as mkdir52, writeFile as writeFile10 } from "fs/promises";
4594
+ import { mkdir as mkdir52, writeFile as writeFile102 } from "fs/promises";
4511
4595
  import { existsSync as existsSync17 } from "fs";
4512
4596
  import path72 from "path";
4513
4597
  import {
@@ -4552,7 +4636,7 @@ import {
4552
4636
  inferModulesFromPaths as inferModulesFromPaths2,
4553
4637
  isAutoPromoteEligible,
4554
4638
  isDecaying,
4555
- isRetiredMemory,
4639
+ isRetiredMemory as isRetiredMemory2,
4556
4640
  literalMatchesAllTokens as literalMatchesAllTokens22,
4557
4641
  literalMatchesAnyToken as literalMatchesAnyToken22,
4558
4642
  loadCodeMap as loadCodeMap5,
@@ -4617,7 +4701,7 @@ import {
4617
4701
  deriveConfidence as deriveConfidence6,
4618
4702
  diffHasDistinctiveOverlap,
4619
4703
  getUsage as getUsage8,
4620
- isRetiredMemory as isRetiredMemory2,
4704
+ isRetiredMemory as isRetiredMemory22,
4621
4705
  loadMemoriesFromDir as loadMemoriesFromDir19,
4622
4706
  loadUsageIndex as loadUsageIndex10,
4623
4707
  literalMatchesAnyToken as literalMatchesAnyToken3,
@@ -4703,15 +4787,15 @@ var BootstrapProjectSaveInputSchema = {
4703
4787
  overwrite: z.boolean().default(false).describe("Overwrite an existing file instead of failing")
4704
4788
  };
4705
4789
  async function bootstrapProjectSave(input, ctx) {
4706
- const target = input.module ? path14.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4707
- const exists = existsSync14(target);
4790
+ const target = input.module ? path15.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4791
+ const exists = existsSync15(target);
4708
4792
  if (exists && !input.overwrite) {
4709
4793
  throw new Error(
4710
4794
  `${target} already exists. Pass overwrite=true to replace it.`
4711
4795
  );
4712
4796
  }
4713
- await mkdir9(path14.dirname(target), { recursive: true });
4714
- await writeFile9(target, input.content, "utf8");
4797
+ await mkdir10(path15.dirname(target), { recursive: true });
4798
+ await writeFile10(target, input.content, "utf8");
4715
4799
  return {
4716
4800
  file_path: target,
4717
4801
  action: exists ? "overwritten" : "created"
@@ -4724,14 +4808,14 @@ var GetProjectContextInputSchema = {
4724
4808
  async function getProjectContext(input, ctx) {
4725
4809
  const out = { root_context: null };
4726
4810
  if (existsSync22(ctx.paths.projectContext)) {
4727
- out.root_context = await readFile8(ctx.paths.projectContext, "utf8");
4811
+ out.root_context = await readFile9(ctx.paths.projectContext, "utf8");
4728
4812
  }
4729
4813
  if (input.module) {
4730
4814
  const modFile = path22.join(ctx.paths.modulesContextDir, input.module, "context.md");
4731
4815
  if (existsSync22(modFile)) {
4732
4816
  out.module_context = {
4733
4817
  name: input.module,
4734
- content: await readFile8(modFile, "utf8")
4818
+ content: await readFile9(modFile, "utf8")
4735
4819
  };
4736
4820
  }
4737
4821
  }
@@ -4758,7 +4842,7 @@ async function memList(input, ctx) {
4758
4842
  if (!existsSync32(ctx.paths.memoriesDir)) {
4759
4843
  return { memories: [] };
4760
4844
  }
4761
- const all = await loadMemoriesFromDir4(ctx.paths.memoriesDir);
4845
+ const all = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
4762
4846
  const filtered = all.filter(({ memory: memory2 }) => {
4763
4847
  const fm = memory2.frontmatter;
4764
4848
  if (input.scope && fm.scope !== input.scope) return false;
@@ -5252,7 +5336,7 @@ async function memReject(input, ctx) {
5252
5336
  if (!existsSync72(ctx.paths.memoriesDir)) {
5253
5337
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
5254
5338
  }
5255
- const memories = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
5339
+ const memories = await loadMemoriesFromDir52(ctx.paths.memoriesDir);
5256
5340
  const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
5257
5341
  if (!loaded) throw new Error(`No memory with id "${input.id}".`);
5258
5342
  await writeFile42(
@@ -5645,7 +5729,7 @@ var MemTriedInputSchema = {
5645
5729
  author: z15.string().optional().describe("Author handle or email")
5646
5730
  };
5647
5731
  async function memTried(input, ctx) {
5648
- if (!existsSync15(ctx.paths.haiveDir)) {
5732
+ if (!existsSync152(ctx.paths.haiveDir)) {
5649
5733
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
5650
5734
  }
5651
5735
  const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
@@ -5671,7 +5755,7 @@ async function memTried(input, ctx) {
5671
5755
  }
5672
5756
  const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
5673
5757
  await mkdir32(path52.dirname(file), { recursive: true });
5674
- if (existsSync15(file)) {
5758
+ if (existsSync152(file)) {
5675
5759
  throw new Error(`Memory already exists at ${file}`);
5676
5760
  }
5677
5761
  await writeFile82(file, serializeMemory7({ frontmatter, body }), "utf8");
@@ -5805,7 +5889,7 @@ async function memObserve(input, ctx) {
5805
5889
  if (existsSync17(file)) {
5806
5890
  throw new Error(`Memory already exists at ${file}`);
5807
5891
  }
5808
- await writeFile10(file, serializeMemory9({ frontmatter, body }), "utf8");
5892
+ await writeFile102(file, serializeMemory9({ frontmatter, body }), "utf8");
5809
5893
  return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
5810
5894
  }
5811
5895
  function pendingDistillPath(ctx) {
@@ -6257,7 +6341,7 @@ async function getBriefing(input, ctx) {
6257
6341
  const s = memory2.frontmatter.status;
6258
6342
  if (s === "rejected" || s === "deprecated") return false;
6259
6343
  if (!input.include_stale && s === "stale") return false;
6260
- if (!input.include_stale && isRetiredMemory(memory2.frontmatter, memory2.body)) return false;
6344
+ if (!input.include_stale && isRetiredMemory2(memory2.frontmatter, memory2.body)) return false;
6261
6345
  if (memory2.frontmatter.type === "session_recap") return false;
6262
6346
  return true;
6263
6347
  });
@@ -7125,7 +7209,7 @@ async function antiPatternsCheck(input, ctx) {
7125
7209
  const t = memory2.frontmatter.type;
7126
7210
  if (t !== "attempt" && t !== "gotcha") return false;
7127
7211
  const s = memory2.frontmatter.status;
7128
- return s !== "rejected" && s !== "deprecated" && s !== "stale" && !isRetiredMemory2(memory2.frontmatter, memory2.body);
7212
+ return s !== "rejected" && s !== "deprecated" && s !== "stale" && !isRetiredMemory22(memory2.frontmatter, memory2.body);
7129
7213
  });
7130
7214
  if (negative.length === 0) {
7131
7215
  return { scanned: 0, warnings: [], notice: "No attempt/gotcha memories found yet." };
@@ -7814,8 +7898,17 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
7814
7898
  repair_command: repairCommand
7815
7899
  };
7816
7900
  }
7901
+ if (warning.has_sensor && !warning.reasons.includes("sensor") && !warning.reasons.includes("anchor")) {
7902
+ return {
7903
+ ...warning,
7904
+ level: "info",
7905
+ rationale: "memory has a deterministic sensor that did not fire and is not anchored to a touched file \u2014 treated as non-violation noise",
7906
+ affected_files: affectedFiles,
7907
+ repair_command: repairCommand
7908
+ };
7909
+ }
7817
7910
  const corroborated = warning.reasons.includes("anchor") || warning.distinctive_literal === true;
7818
- const semanticReviewFloor = corroborated ? 0.45 : 0.6;
7911
+ const semanticReviewFloor = corroborated ? 0.45 : 0.65;
7819
7912
  if (hasSemantic && semanticScore >= semanticReviewFloor || highConfidence && warning.reasons.includes("anchor") && warning.reasons.includes("literal")) {
7820
7913
  return {
7821
7914
  ...warning,
@@ -8491,7 +8584,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
8491
8584
  };
8492
8585
  }
8493
8586
  var SERVER_NAME = "haive";
8494
- var SERVER_VERSION = "0.18.0";
8587
+ var SERVER_VERSION = "0.20.0";
8495
8588
  function jsonResult(data) {
8496
8589
  return {
8497
8590
  content: [
@@ -9523,8 +9616,8 @@ function registerMcp(program2) {
9523
9616
 
9524
9617
  // src/commands/sync.ts
9525
9618
  import { spawnSync as spawnSync4 } from "child_process";
9526
- import { readFile as readFile10, writeFile as writeFile16, mkdir as mkdir11 } from "fs/promises";
9527
- import { existsSync as existsSync34 } from "fs";
9619
+ import { readFile as readFile10, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
9620
+ import { existsSync as existsSync33 } from "fs";
9528
9621
  import path16 from "path";
9529
9622
  import "commander";
9530
9623
  import {
@@ -9537,7 +9630,7 @@ import {
9537
9630
  isStackPackSeed,
9538
9631
  loadCodeMap as loadCodeMap6,
9539
9632
  loadConfig as loadConfig4,
9540
- loadMemoriesFromDir as loadMemoriesFromDir26,
9633
+ loadMemoriesFromDir as loadMemoriesFromDir25,
9541
9634
  loadUsageIndex as loadUsageIndex13,
9542
9635
  pullCrossRepoSources,
9543
9636
  resolveHaivePaths as resolveHaivePaths9,
@@ -9547,90 +9640,7 @@ import {
9547
9640
  verifyAnchor as verifyAnchor2,
9548
9641
  watchContracts
9549
9642
  } from "@hiveai/core";
9550
- import { BRIDGE_TARGETS } from "@hiveai/core";
9551
-
9552
- // src/utils/bridge-files.ts
9553
- import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile15 } from "fs/promises";
9554
- import { existsSync as existsSync33 } from "fs";
9555
- import path15 from "path";
9556
- import {
9557
- BRIDGE_MARKERS,
9558
- generateBridges,
9559
- isRetiredMemory as isRetiredMemory3,
9560
- loadMemoriesFromDir as loadMemoriesFromDir25
9561
- } from "@hiveai/core";
9562
- async function writeBridgeFiles(root, paths, opts) {
9563
- const result = { created: [], updated: [], unchanged: [] };
9564
- if (!existsSync33(paths.memoriesDir)) return result;
9565
- const allLoaded = await loadMemoriesFromDir25(paths.memoriesDir);
9566
- const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory3(m.frontmatter, m.body));
9567
- const sensors = [];
9568
- for (const m of memories) {
9569
- const sensor = m.frontmatter.sensor;
9570
- if (!sensor || sensor.severity !== "block") continue;
9571
- sensors.push({
9572
- id: m.frontmatter.id,
9573
- severity: "block",
9574
- message: sensor.message,
9575
- ...sensor.pattern ? { pattern: sensor.pattern } : {},
9576
- paths: sensor.paths.length > 0 ? sensor.paths : m.frontmatter.anchor.paths
9577
- });
9578
- }
9579
- const maxMemories = Math.max(1, opts.maxMemories ?? 8);
9580
- const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
9581
- for (const output of outputs) {
9582
- const targetFile = path15.join(root, output.path);
9583
- const fileExists = existsSync33(targetFile);
9584
- if (opts.onlyExisting && !fileExists) continue;
9585
- if (opts.dryRun) {
9586
- (fileExists ? result.updated : result.created).push(output.path);
9587
- continue;
9588
- }
9589
- await mkdir10(path15.dirname(targetFile), { recursive: true });
9590
- if (!fileExists) {
9591
- await writeFile15(targetFile, output.content, "utf8");
9592
- result.created.push(output.path);
9593
- continue;
9594
- }
9595
- let existing = (await readFile9(targetFile, "utf8")).replace(/\r\n/g, "\n");
9596
- const withMemories = replaceMarkerBlock(
9597
- existing,
9598
- BRIDGE_MARKERS.memoriesStart,
9599
- BRIDGE_MARKERS.memoriesEnd,
9600
- extractMarkerBlock(output.content, BRIDGE_MARKERS.memoriesStart, BRIDGE_MARKERS.memoriesEnd)
9601
- );
9602
- const sensorsBlockContent = extractMarkerBlock(
9603
- output.content,
9604
- BRIDGE_MARKERS.sensorsStart,
9605
- BRIDGE_MARKERS.sensorsEnd
9606
- );
9607
- const withSensors = sensorsBlockContent ? replaceMarkerBlock(withMemories, BRIDGE_MARKERS.sensorsStart, BRIDGE_MARKERS.sensorsEnd, sensorsBlockContent) : withMemories;
9608
- if (withSensors === existing) {
9609
- result.unchanged.push(output.path);
9610
- continue;
9611
- }
9612
- await writeFile15(targetFile, withSensors, "utf8");
9613
- result.updated.push(output.path);
9614
- }
9615
- return result;
9616
- }
9617
- function extractMarkerBlock(text, startMarker, endMarker) {
9618
- const startIdx = text.indexOf(startMarker);
9619
- const endIdx = text.indexOf(endMarker);
9620
- if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;
9621
- return text.slice(startIdx, endIdx + endMarker.length);
9622
- }
9623
- function replaceMarkerBlock(existing, startMarker, endMarker, replacement) {
9624
- if (!replacement) return existing;
9625
- const startIdx = existing.indexOf(startMarker);
9626
- const endIdx = existing.indexOf(endMarker);
9627
- if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
9628
- return existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + replacement + "\n";
9629
- }
9630
- return existing.slice(0, startIdx) + replacement + existing.slice(endIdx + endMarker.length);
9631
- }
9632
-
9633
- // src/commands/sync.ts
9643
+ import { BRIDGE_TARGETS as BRIDGE_TARGETS2 } from "@hiveai/core";
9634
9644
  var BRIDGE_START = "<!-- haive:memories-start -->";
9635
9645
  var BRIDGE_END = "<!-- haive:memories-end -->";
9636
9646
  function registerSync(program2) {
@@ -9645,7 +9655,7 @@ function registerSync(program2) {
9645
9655
  ).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) => {
9646
9656
  const root = findProjectRoot12(opts.dir);
9647
9657
  const paths = resolveHaivePaths9(root);
9648
- if (!existsSync34(paths.memoriesDir)) {
9658
+ if (!existsSync33(paths.memoriesDir)) {
9649
9659
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9650
9660
  process.exitCode = 1;
9651
9661
  return;
@@ -9664,12 +9674,12 @@ function registerSync(program2) {
9664
9674
  let promoted = 0;
9665
9675
  let autoApproved = 0;
9666
9676
  if (opts.verify !== false) {
9667
- const memories = await loadMemoriesFromDir26(paths.memoriesDir);
9677
+ const memories = await loadMemoriesFromDir25(paths.memoriesDir);
9668
9678
  for (const { memory: memory2, filePath } of memories) {
9669
9679
  if (memory2.frontmatter.type === "session_recap") {
9670
9680
  if (memory2.frontmatter.status === "stale") {
9671
9681
  if (!dryRun) {
9672
- await writeFile16(
9682
+ await writeFile15(
9673
9683
  filePath,
9674
9684
  serializeMemory13({
9675
9685
  frontmatter: {
@@ -9694,7 +9704,7 @@ function registerSync(program2) {
9694
9704
  if (result.stale) {
9695
9705
  if (memory2.frontmatter.status !== "stale") {
9696
9706
  if (!dryRun) {
9697
- await writeFile16(
9707
+ await writeFile15(
9698
9708
  filePath,
9699
9709
  serializeMemory13({
9700
9710
  frontmatter: {
@@ -9712,7 +9722,7 @@ function registerSync(program2) {
9712
9722
  }
9713
9723
  } else if (memory2.frontmatter.status === "stale") {
9714
9724
  if (!dryRun) {
9715
- await writeFile16(
9725
+ await writeFile15(
9716
9726
  filePath,
9717
9727
  serializeMemory13({
9718
9728
  frontmatter: {
@@ -9731,7 +9741,7 @@ function registerSync(program2) {
9731
9741
  }
9732
9742
  }
9733
9743
  if (opts.promote !== false) {
9734
- const memories = await loadMemoriesFromDir26(paths.memoriesDir);
9744
+ const memories = await loadMemoriesFromDir25(paths.memoriesDir);
9735
9745
  const usage = await loadUsageIndex13(paths);
9736
9746
  const nowMs = Date.now();
9737
9747
  for (const { memory: memory2, filePath } of memories) {
@@ -9742,7 +9752,7 @@ function registerSync(program2) {
9742
9752
  maxRejections: DEFAULT_AUTO_PROMOTE_RULE2.maxRejections
9743
9753
  })) {
9744
9754
  if (!dryRun) {
9745
- await writeFile16(
9755
+ await writeFile15(
9746
9756
  filePath,
9747
9757
  serializeMemory13({ frontmatter: { ...fm, status: "validated" }, body: memory2.body }),
9748
9758
  "utf8"
@@ -9755,7 +9765,7 @@ function registerSync(program2) {
9755
9765
  const ageHours = (nowMs - new Date(fm.created_at).getTime()) / (1e3 * 60 * 60);
9756
9766
  if (ageHours >= autoApproveDelayHours) {
9757
9767
  if (!dryRun) {
9758
- await writeFile16(
9768
+ await writeFile15(
9759
9769
  filePath,
9760
9770
  serializeMemory13({
9761
9771
  frontmatter: {
@@ -9783,7 +9793,7 @@ function registerSync(program2) {
9783
9793
  for (const repair of repairs) log(ui.dim(`autopilot: ${repair.message}`));
9784
9794
  }
9785
9795
  const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
9786
- const draftMemories = (await loadMemoriesFromDir26(paths.memoriesDir)).filter(
9796
+ const draftMemories = (await loadMemoriesFromDir25(paths.memoriesDir)).filter(
9787
9797
  (m) => m.memory.frontmatter.status === "draft"
9788
9798
  );
9789
9799
  const draftCount = draftMemories.length;
@@ -9806,14 +9816,14 @@ function registerSync(program2) {
9806
9816
  } else {
9807
9817
  const agentsMd = path16.join(root, "AGENTS.md");
9808
9818
  bridgeTargets = [path16.join(root, "CLAUDE.md")];
9809
- if (existsSync34(agentsMd)) bridgeTargets.push(agentsMd);
9819
+ if (existsSync33(agentsMd)) bridgeTargets.push(agentsMd);
9810
9820
  }
9811
9821
  for (const bridgeFile of bridgeTargets) {
9812
9822
  await injectBridge(bridgeFile, paths.memoriesDir, maxInject, root, opts.quiet);
9813
9823
  }
9814
9824
  }
9815
9825
  if (opts.noBridges !== true) {
9816
- const nativeTargets = BRIDGE_TARGETS.filter(
9826
+ const nativeTargets = BRIDGE_TARGETS2.filter(
9817
9827
  (t) => t !== "claude" && t !== "agents"
9818
9828
  );
9819
9829
  try {
@@ -9849,7 +9859,7 @@ function registerSync(program2) {
9849
9859
  }
9850
9860
  }
9851
9861
  if (!opts.quiet) {
9852
- const allForDecay = await loadMemoriesFromDir26(paths.memoriesDir);
9862
+ const allForDecay = await loadMemoriesFromDir25(paths.memoriesDir);
9853
9863
  const usageForDecay = await loadUsageIndex13(paths);
9854
9864
  const decaying = allForDecay.filter(({ memory: memory2 }) => {
9855
9865
  const fm = memory2.frontmatter;
@@ -9939,7 +9949,7 @@ Wait for **explicit confirmation** before acting.
9939
9949
  if (!dryRun) {
9940
9950
  const teamDir = path16.join(paths.memoriesDir, "team");
9941
9951
  await mkdir11(teamDir, { recursive: true });
9942
- await writeFile16(
9952
+ await writeFile15(
9943
9953
  path16.join(teamDir, `${fm.id}.md`),
9944
9954
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
9945
9955
  "utf8"
@@ -10008,7 +10018,7 @@ Wait for **explicit confirmation** before acting.
10008
10018
  if (!dryRun) {
10009
10019
  const teamDir = path16.join(paths.memoriesDir, "team");
10010
10020
  await mkdir11(teamDir, { recursive: true });
10011
- await writeFile16(
10021
+ await writeFile15(
10012
10022
  path16.join(teamDir, `${fm.id}.md`),
10013
10023
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
10014
10024
  "utf8"
@@ -10100,8 +10110,8 @@ function bridgeSummaryLine(body) {
10100
10110
  return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
10101
10111
  }
10102
10112
  async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
10103
- if (!existsSync34(memoriesDir)) return;
10104
- const all = await loadMemoriesFromDir26(memoriesDir);
10113
+ if (!existsSync33(memoriesDir)) return;
10114
+ const all = await loadMemoriesFromDir25(memoriesDir);
10105
10115
  const top = all.filter(({ memory: memory2 }) => {
10106
10116
  const s = memory2.frontmatter.status;
10107
10117
  if (memory2.frontmatter.type === "session_recap") return false;
@@ -10126,7 +10136,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
10126
10136
  ` + block + `
10127
10137
 
10128
10138
  ${BRIDGE_END}`;
10129
- const fileExists = existsSync34(bridgeFile);
10139
+ const fileExists = existsSync33(bridgeFile);
10130
10140
  let existing = fileExists ? await readFile10(bridgeFile, "utf8") : "";
10131
10141
  existing = existing.replace(/\r\n/g, "\n");
10132
10142
  const startIdx = existing.indexOf(BRIDGE_START);
@@ -10148,7 +10158,7 @@ ${BRIDGE_END}`;
10148
10158
  }
10149
10159
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
10150
10160
  }
10151
- await writeFile16(bridgeFile, updated, "utf8");
10161
+ await writeFile15(bridgeFile, updated, "utf8");
10152
10162
  if (!quiet) {
10153
10163
  console.log(
10154
10164
  ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path16.relative(root, bridgeFile)}`)
@@ -10176,8 +10186,8 @@ function collectSinceChanges(root, ref) {
10176
10186
 
10177
10187
  // src/commands/memory-add.ts
10178
10188
  import { createHash as createHash2 } from "crypto";
10179
- import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile17 } from "fs/promises";
10180
- import { existsSync as existsSync35 } from "fs";
10189
+ import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile16 } from "fs/promises";
10190
+ import { existsSync as existsSync34 } from "fs";
10181
10191
  import path17 from "path";
10182
10192
  import "commander";
10183
10193
  import {
@@ -10185,7 +10195,7 @@ import {
10185
10195
  findProjectRoot as findProjectRoot13,
10186
10196
  inferModulesFromPaths as inferModulesFromPaths3,
10187
10197
  loadConfig as loadConfig5,
10188
- loadMemoriesFromDir as loadMemoriesFromDir27,
10198
+ loadMemoriesFromDir as loadMemoriesFromDir26,
10189
10199
  memoryFilePath as memoryFilePath7,
10190
10200
  resolveHaivePaths as resolveHaivePaths10,
10191
10201
  serializeMemory as serializeMemory14,
@@ -10219,7 +10229,7 @@ function registerMemoryAdd(memory2) {
10219
10229
  ).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("--activation-keyword <csv>", "skill only: comma-separated keywords that trigger progressive disclosure of this skill").option("--activation-glob <csv>", "skill only: comma-separated path globs that trigger this skill").option("--activation-always", "skill only: always surface this skill (no triggers needed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
10220
10230
  const root = findProjectRoot13(opts.dir);
10221
10231
  const paths = resolveHaivePaths10(root);
10222
- if (!existsSync35(paths.haiveDir)) {
10232
+ if (!existsSync34(paths.haiveDir)) {
10223
10233
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10224
10234
  process.exitCode = 1;
10225
10235
  return;
@@ -10236,7 +10246,7 @@ function registerMemoryAdd(memory2) {
10236
10246
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
10237
10247
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
10238
10248
  if (anchorPaths.length > 0) {
10239
- const missing = anchorPaths.filter((p) => !existsSync35(path17.resolve(root, p)));
10249
+ const missing = anchorPaths.filter((p) => !existsSync34(path17.resolve(root, p)));
10240
10250
  if (missing.length > 0) {
10241
10251
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
10242
10252
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -10249,7 +10259,7 @@ function registerMemoryAdd(memory2) {
10249
10259
  const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
10250
10260
  let body;
10251
10261
  if (opts.bodyFile !== void 0) {
10252
- if (!existsSync35(opts.bodyFile)) {
10262
+ if (!existsSync34(opts.bodyFile)) {
10253
10263
  ui.error(`--body-file not found: ${opts.bodyFile}`);
10254
10264
  process.exitCode = 1;
10255
10265
  return;
@@ -10265,9 +10275,9 @@ TODO \u2014 write the memory body.
10265
10275
  `;
10266
10276
  }
10267
10277
  const scope = opts.scope ?? config.defaultScope ?? "personal";
10268
- if (existsSync35(paths.memoriesDir)) {
10278
+ if (existsSync34(paths.memoriesDir)) {
10269
10279
  const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
10270
- const allForHash = await loadMemoriesFromDir27(paths.memoriesDir);
10280
+ const allForHash = await loadMemoriesFromDir26(paths.memoriesDir);
10271
10281
  const hashDup = allForHash.find(
10272
10282
  ({ memory: memory3 }) => createHash2("sha256").update(memory3.body.trim()).digest("hex").slice(0, 12) === incomingHash && memory3.frontmatter.scope === scope
10273
10283
  );
@@ -10278,8 +10288,8 @@ TODO \u2014 write the memory body.
10278
10288
  return;
10279
10289
  }
10280
10290
  }
10281
- if (opts.topic && existsSync35(paths.memoriesDir)) {
10282
- const existing = await loadMemoriesFromDir27(paths.memoriesDir);
10291
+ if (opts.topic && existsSync34(paths.memoriesDir)) {
10292
+ const existing = await loadMemoriesFromDir26(paths.memoriesDir);
10283
10293
  const topicMatch = existing.find(
10284
10294
  ({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
10285
10295
  );
@@ -10299,7 +10309,7 @@ TODO \u2014 write the memory body.
10299
10309
  };
10300
10310
  const suggestedSensor = !newFrontmatter.sensor ? suggestSensorForCliMemory(opts.type, body, newFrontmatter.anchor.paths) : null;
10301
10311
  if (suggestedSensor) newFrontmatter.sensor = suggestedSensor;
10302
- await writeFile17(topicMatch.filePath, serializeMemory14({ frontmatter: newFrontmatter, body }), "utf8");
10312
+ await writeFile16(topicMatch.filePath, serializeMemory14({ frontmatter: newFrontmatter, body }), "utf8");
10303
10313
  ui.success(`Updated (topic upsert) ${path17.relative(root, topicMatch.filePath)}`);
10304
10314
  ui.info(`id=${fm.id} revision=${revisionCount}`);
10305
10315
  if (suggestedSensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(suggestedSensor.pattern)}`);
@@ -10325,13 +10335,13 @@ TODO \u2014 write the memory body.
10325
10335
  });
10326
10336
  const file = memoryFilePath7(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
10327
10337
  await mkdir12(path17.dirname(file), { recursive: true });
10328
- if (existsSync35(file)) {
10338
+ if (existsSync34(file)) {
10329
10339
  ui.error(`Memory already exists at ${file}`);
10330
10340
  process.exitCode = 1;
10331
10341
  return;
10332
10342
  }
10333
- if (existsSync35(paths.memoriesDir)) {
10334
- const existing = await loadMemoriesFromDir27(paths.memoriesDir);
10343
+ if (existsSync34(paths.memoriesDir)) {
10344
+ const existing = await loadMemoriesFromDir26(paths.memoriesDir);
10335
10345
  const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
10336
10346
  const similar = existing.filter(({ memory: memory3 }) => {
10337
10347
  const id = memory3.frontmatter.id.toLowerCase();
@@ -10342,7 +10352,7 @@ TODO \u2014 write the memory body.
10342
10352
  ui.warn("Consider updating one of these with `haive memory update` instead.");
10343
10353
  }
10344
10354
  }
10345
- await writeFile17(file, serializeMemory14({ frontmatter, body }), "utf8");
10355
+ await writeFile16(file, serializeMemory14({ frontmatter, body }), "utf8");
10346
10356
  ui.success(`Created ${path17.relative(root, file)}`);
10347
10357
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
10348
10358
  if (frontmatter.sensor?.autogen) {
@@ -10422,14 +10432,14 @@ function slugify(value) {
10422
10432
  }
10423
10433
 
10424
10434
  // src/commands/memory-list.ts
10425
- import { existsSync as existsSync36 } from "fs";
10435
+ import { existsSync as existsSync35 } from "fs";
10426
10436
  import path18 from "path";
10427
10437
  import "commander";
10428
10438
  import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
10429
10439
 
10430
10440
  // src/utils/fs.ts
10431
10441
  import {
10432
- loadMemoriesFromDir as loadMemoriesFromDir28,
10442
+ loadMemoriesFromDir as loadMemoriesFromDir27,
10433
10443
  loadMemory,
10434
10444
  listMarkdownFilesRecursive
10435
10445
  } from "@hiveai/core";
@@ -10439,12 +10449,12 @@ function registerMemoryList(memory2) {
10439
10449
  memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("--limit <n>", "max memories to display").option("-d, --dir <dir>", "project root").action(async (opts) => {
10440
10450
  const root = findProjectRoot14(opts.dir);
10441
10451
  const paths = resolveHaivePaths11(root);
10442
- if (!existsSync36(paths.memoriesDir)) {
10452
+ if (!existsSync35(paths.memoriesDir)) {
10443
10453
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
10444
10454
  process.exitCode = 1;
10445
10455
  return;
10446
10456
  }
10447
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
10457
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
10448
10458
  const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
10449
10459
  const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10)) : void 0;
10450
10460
  const filtered = all.filter((m) => {
@@ -10513,8 +10523,8 @@ function matchesFilters(loaded, opts) {
10513
10523
  }
10514
10524
 
10515
10525
  // src/commands/memory-promote.ts
10516
- import { mkdir as mkdir13, unlink as unlink2, writeFile as writeFile18 } from "fs/promises";
10517
- import { existsSync as existsSync37 } from "fs";
10526
+ import { mkdir as mkdir13, unlink as unlink2, writeFile as writeFile17 } from "fs/promises";
10527
+ import { existsSync as existsSync36 } from "fs";
10518
10528
  import path19 from "path";
10519
10529
  import "commander";
10520
10530
  import {
@@ -10527,12 +10537,12 @@ function registerMemoryPromote(memory2) {
10527
10537
  memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
10528
10538
  const root = findProjectRoot15(opts.dir);
10529
10539
  const paths = resolveHaivePaths12(root);
10530
- if (!existsSync37(paths.memoriesDir)) {
10540
+ if (!existsSync36(paths.memoriesDir)) {
10531
10541
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
10532
10542
  process.exitCode = 1;
10533
10543
  return;
10534
10544
  }
10535
- const teamAndModule = await loadMemoriesFromDir28(paths.memoriesDir);
10545
+ const teamAndModule = await loadMemoriesFromDir27(paths.memoriesDir);
10536
10546
  const alreadyShared = teamAndModule.find(
10537
10547
  (m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
10538
10548
  );
@@ -10546,7 +10556,7 @@ function registerMemoryPromote(memory2) {
10546
10556
  }
10547
10557
  return;
10548
10558
  }
10549
- const all = await loadMemoriesFromDir28(paths.personalDir);
10559
+ const all = await loadMemoriesFromDir27(paths.personalDir);
10550
10560
  const found = all.find((m) => m.memory.frontmatter.id === id);
10551
10561
  if (!found) {
10552
10562
  ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
@@ -10563,7 +10573,7 @@ function registerMemoryPromote(memory2) {
10563
10573
  };
10564
10574
  const newPath = memoryFilePath8(paths, "team", updated.frontmatter.id);
10565
10575
  await mkdir13(path19.dirname(newPath), { recursive: true });
10566
- await writeFile18(newPath, serializeMemory15(updated), "utf8");
10576
+ await writeFile17(newPath, serializeMemory15(updated), "utf8");
10567
10577
  await unlink2(found.filePath);
10568
10578
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
10569
10579
  ui.info(`Now at ${path19.relative(root, newPath)}`);
@@ -10572,8 +10582,8 @@ function registerMemoryPromote(memory2) {
10572
10582
  }
10573
10583
 
10574
10584
  // src/commands/memory-approve.ts
10575
- import { existsSync as existsSync38 } from "fs";
10576
- import { writeFile as writeFile19 } from "fs/promises";
10585
+ import { existsSync as existsSync37 } from "fs";
10586
+ import { writeFile as writeFile18 } from "fs/promises";
10577
10587
  import path20 from "path";
10578
10588
  import "commander";
10579
10589
  import {
@@ -10585,12 +10595,12 @@ function registerMemoryApprove(memory2) {
10585
10595
  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) => {
10586
10596
  const root = findProjectRoot16(opts.dir);
10587
10597
  const paths = resolveHaivePaths13(root);
10588
- if (!existsSync38(paths.memoriesDir)) {
10598
+ if (!existsSync37(paths.memoriesDir)) {
10589
10599
  ui.error(`No .ai/memories at ${root}.`);
10590
10600
  process.exitCode = 1;
10591
10601
  return;
10592
10602
  }
10593
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
10603
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
10594
10604
  if (opts.all || opts.pending) {
10595
10605
  const candidates = all.filter((m) => {
10596
10606
  const s = m.memory.frontmatter.status;
@@ -10607,7 +10617,7 @@ function registerMemoryApprove(memory2) {
10607
10617
  frontmatter: { ...found2.memory.frontmatter, status: "validated" },
10608
10618
  body: found2.memory.body
10609
10619
  };
10610
- await writeFile19(found2.filePath, serializeMemory16(next2), "utf8");
10620
+ await writeFile18(found2.filePath, serializeMemory16(next2), "utf8");
10611
10621
  count++;
10612
10622
  }
10613
10623
  ui.success(`Approved ${count} memor${count === 1 ? "y" : "ies"} (status=validated)`);
@@ -10636,15 +10646,15 @@ function registerMemoryApprove(memory2) {
10636
10646
  frontmatter: { ...found.memory.frontmatter, status: "validated" },
10637
10647
  body: found.memory.body
10638
10648
  };
10639
- await writeFile19(found.filePath, serializeMemory16(next), "utf8");
10649
+ await writeFile18(found.filePath, serializeMemory16(next), "utf8");
10640
10650
  ui.success(`Approved ${id} (status=validated)`);
10641
10651
  ui.info(path20.relative(root, found.filePath));
10642
10652
  });
10643
10653
  }
10644
10654
 
10645
10655
  // src/commands/memory-update.ts
10646
- import { readFile as readFile12, writeFile as writeFile20 } from "fs/promises";
10647
- import { existsSync as existsSync39 } from "fs";
10656
+ import { readFile as readFile12, writeFile as writeFile19 } from "fs/promises";
10657
+ import { existsSync as existsSync38 } from "fs";
10648
10658
  import path21 from "path";
10649
10659
  import "commander";
10650
10660
  import {
@@ -10656,12 +10666,12 @@ function registerMemoryUpdate(memory2) {
10656
10666
  memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--body-file <path>", "read new body from a Markdown file \u2014 for long content").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
10657
10667
  const root = findProjectRoot17(opts.dir);
10658
10668
  const paths = resolveHaivePaths14(root);
10659
- if (!existsSync39(paths.memoriesDir)) {
10669
+ if (!existsSync38(paths.memoriesDir)) {
10660
10670
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10661
10671
  process.exitCode = 1;
10662
10672
  return;
10663
10673
  }
10664
- const memories = await loadMemoriesFromDir28(paths.memoriesDir);
10674
+ const memories = await loadMemoriesFromDir27(paths.memoriesDir);
10665
10675
  const loaded = memories.find((m) => m.memory.frontmatter.id === id);
10666
10676
  if (!loaded) {
10667
10677
  ui.error(`No memory with id "${id}".`);
@@ -10698,7 +10708,7 @@ function registerMemoryUpdate(memory2) {
10698
10708
  if (opts.author !== void 0) updated.push("author");
10699
10709
  let newBody;
10700
10710
  if (opts.bodyFile !== void 0) {
10701
- if (!existsSync39(opts.bodyFile)) {
10711
+ if (!existsSync38(opts.bodyFile)) {
10702
10712
  ui.error(`--body-file not found: ${opts.bodyFile}`);
10703
10713
  process.exitCode = 1;
10704
10714
  return;
@@ -10719,7 +10729,7 @@ function registerMemoryUpdate(memory2) {
10719
10729
  ui.warn("Nothing to update \u2014 provide at least one option.");
10720
10730
  return;
10721
10731
  }
10722
- await writeFile20(
10732
+ await writeFile19(
10723
10733
  loaded.filePath,
10724
10734
  serializeMemory17({ frontmatter: newFrontmatter, body: newBody }),
10725
10735
  "utf8"
@@ -10743,8 +10753,8 @@ function parseCsv3(value) {
10743
10753
  }
10744
10754
 
10745
10755
  // src/commands/memory-auto-promote.ts
10746
- import { writeFile as writeFile21 } from "fs/promises";
10747
- import { existsSync as existsSync40 } from "fs";
10756
+ import { writeFile as writeFile20 } from "fs/promises";
10757
+ import { existsSync as existsSync39 } from "fs";
10748
10758
  import path23 from "path";
10749
10759
  import "commander";
10750
10760
  import {
@@ -10764,7 +10774,7 @@ function registerMemoryAutoPromote(memory2) {
10764
10774
  ).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
10765
10775
  const root = findProjectRoot18(opts.dir);
10766
10776
  const paths = resolveHaivePaths15(root);
10767
- if (!existsSync40(paths.memoriesDir)) {
10777
+ if (!existsSync39(paths.memoriesDir)) {
10768
10778
  ui.error(`No .ai/memories at ${root}.`);
10769
10779
  process.exitCode = 1;
10770
10780
  return;
@@ -10773,7 +10783,7 @@ function registerMemoryAutoPromote(memory2) {
10773
10783
  minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE3.minReads),
10774
10784
  maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
10775
10785
  };
10776
- const memories = await loadMemoriesFromDir28(paths.memoriesDir);
10786
+ const memories = await loadMemoriesFromDir27(paths.memoriesDir);
10777
10787
  const usage = await loadUsageIndex14(paths);
10778
10788
  const eligible = memories.filter(
10779
10789
  ({ memory: memory3 }) => isAutoPromoteEligible3(memory3.frontmatter, getUsage12(usage, memory3.frontmatter.id), rule)
@@ -10796,7 +10806,7 @@ function registerMemoryAutoPromote(memory2) {
10796
10806
  frontmatter: { ...mem.frontmatter, status: "validated" },
10797
10807
  body: mem.body
10798
10808
  };
10799
- await writeFile21(filePath, serializeMemory18(next), "utf8");
10809
+ await writeFile20(filePath, serializeMemory18(next), "utf8");
10800
10810
  written++;
10801
10811
  }
10802
10812
  }
@@ -10807,7 +10817,7 @@ function registerMemoryAutoPromote(memory2) {
10807
10817
 
10808
10818
  // src/commands/memory-edit.ts
10809
10819
  import { spawn as spawn3 } from "child_process";
10810
- import { existsSync as existsSync41 } from "fs";
10820
+ import { existsSync as existsSync40 } from "fs";
10811
10821
  import { readFile as readFile13 } from "fs/promises";
10812
10822
  import path24 from "path";
10813
10823
  import "commander";
@@ -10820,12 +10830,12 @@ function registerMemoryEdit(memory2) {
10820
10830
  memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
10821
10831
  const root = findProjectRoot19(opts.dir);
10822
10832
  const paths = resolveHaivePaths16(root);
10823
- if (!existsSync41(paths.memoriesDir)) {
10833
+ if (!existsSync40(paths.memoriesDir)) {
10824
10834
  ui.error(`No .ai/memories at ${root}.`);
10825
10835
  process.exitCode = 1;
10826
10836
  return;
10827
10837
  }
10828
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
10838
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
10829
10839
  const found = all.find((m) => m.memory.frontmatter.id === id);
10830
10840
  if (!found) {
10831
10841
  ui.error(`No memory with id "${id}".`);
@@ -10860,7 +10870,7 @@ function runEditor(editor, file) {
10860
10870
  }
10861
10871
 
10862
10872
  // src/commands/memory-for-files.ts
10863
- import { existsSync as existsSync43 } from "fs";
10873
+ import { existsSync as existsSync41 } from "fs";
10864
10874
  import path25 from "path";
10865
10875
  import "commander";
10866
10876
  import {
@@ -10876,12 +10886,12 @@ function registerMemoryForFiles(memory2) {
10876
10886
  memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
10877
10887
  const root = findProjectRoot20(opts.dir);
10878
10888
  const paths = resolveHaivePaths17(root);
10879
- if (!existsSync43(paths.memoriesDir)) {
10889
+ if (!existsSync41(paths.memoriesDir)) {
10880
10890
  ui.error(`No .ai/memories at ${root}.`);
10881
10891
  process.exitCode = 1;
10882
10892
  return;
10883
10893
  }
10884
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
10894
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
10885
10895
  const usage = await loadUsageIndex15(paths);
10886
10896
  const inferred = inferModulesFromPaths4(files);
10887
10897
  const byAnchor = [];
@@ -10988,7 +10998,7 @@ function printGroup(root, label, loaded, usage) {
10988
10998
  }
10989
10999
 
10990
11000
  // src/commands/memory-hot.ts
10991
- import { existsSync as existsSync44 } from "fs";
11001
+ import { existsSync as existsSync43 } from "fs";
10992
11002
  import path26 from "path";
10993
11003
  import "commander";
10994
11004
  import {
@@ -11003,13 +11013,13 @@ function registerMemoryHot(memory2) {
11003
11013
  ).option("--threshold <n>", "minimum read_count to qualify (default: 3)", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
11004
11014
  const root = findProjectRoot21(opts.dir);
11005
11015
  const paths = resolveHaivePaths18(root);
11006
- if (!existsSync44(paths.memoriesDir)) {
11016
+ if (!existsSync43(paths.memoriesDir)) {
11007
11017
  ui.error(`No .ai/memories at ${root}.`);
11008
11018
  process.exitCode = 1;
11009
11019
  return;
11010
11020
  }
11011
11021
  const threshold = Math.max(1, Number(opts.threshold ?? 3));
11012
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11022
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11013
11023
  const usage = await loadUsageIndex16(paths);
11014
11024
  const candidates = all.filter(({ memory: mem }) => {
11015
11025
  const fm = mem.frontmatter;
@@ -11041,8 +11051,8 @@ function registerMemoryHot(memory2) {
11041
11051
  }
11042
11052
 
11043
11053
  // src/commands/memory-tried.ts
11044
- import { mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
11045
- import { existsSync as existsSync45 } from "fs";
11054
+ import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
11055
+ import { existsSync as existsSync44 } from "fs";
11046
11056
  import path27 from "path";
11047
11057
  import "commander";
11048
11058
  import {
@@ -11072,7 +11082,7 @@ function registerMemoryTried(memory2) {
11072
11082
  ).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("-d, --dir <dir>", "project root").action(async (opts) => {
11073
11083
  const root = findProjectRoot22(opts.dir);
11074
11084
  const paths = resolveHaivePaths19(root);
11075
- if (!existsSync45(paths.haiveDir)) {
11085
+ if (!existsSync44(paths.haiveDir)) {
11076
11086
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11077
11087
  process.exitCode = 1;
11078
11088
  return;
@@ -11100,12 +11110,12 @@ function registerMemoryTried(memory2) {
11100
11110
  }
11101
11111
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
11102
11112
  await mkdir14(path27.dirname(file), { recursive: true });
11103
- if (existsSync45(file)) {
11113
+ if (existsSync44(file)) {
11104
11114
  ui.error(`Memory already exists at ${file}`);
11105
11115
  process.exitCode = 1;
11106
11116
  return;
11107
11117
  }
11108
- await writeFile23(file, serializeMemory19({ frontmatter, body }), "utf8");
11118
+ await writeFile21(file, serializeMemory19({ frontmatter, body }), "utf8");
11109
11119
  ui.success(`Recorded: ${path27.relative(root, file)}`);
11110
11120
  ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
11111
11121
  if (sensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(sensor.pattern)}`);
@@ -11118,7 +11128,7 @@ function parseCsv4(value) {
11118
11128
 
11119
11129
  // src/commands/memory-seed.ts
11120
11130
  import { readFile as readFile14 } from "fs/promises";
11121
- import { existsSync as existsSync46 } from "fs";
11131
+ import { existsSync as existsSync45 } from "fs";
11122
11132
  import path28 from "path";
11123
11133
  import "commander";
11124
11134
  import {
@@ -11158,7 +11168,7 @@ function registerMemorySeed(memory2) {
11158
11168
  }
11159
11169
  return;
11160
11170
  }
11161
- if (!existsSync46(paths.haiveDir)) {
11171
+ if (!existsSync45(paths.haiveDir)) {
11162
11172
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11163
11173
  process.exitCode = 1;
11164
11174
  return;
@@ -11214,7 +11224,7 @@ function registerMemorySeed(memory2) {
11214
11224
  }
11215
11225
 
11216
11226
  // src/commands/memory-pending.ts
11217
- import { existsSync as existsSync47 } from "fs";
11227
+ import { existsSync as existsSync46 } from "fs";
11218
11228
  import path29 from "path";
11219
11229
  import "commander";
11220
11230
  import {
@@ -11227,12 +11237,12 @@ function registerMemoryPending(memory2) {
11227
11237
  memory2.command("pending").description("List draft and proposed memories awaiting review (sorted by reads desc).\n\n draft = created but not yet activated \xB7 proposed = promoted, awaiting team validation").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
11228
11238
  const root = findProjectRoot24(opts.dir);
11229
11239
  const paths = resolveHaivePaths21(root);
11230
- if (!existsSync47(paths.memoriesDir)) {
11240
+ if (!existsSync46(paths.memoriesDir)) {
11231
11241
  ui.error(`No .ai/memories at ${root}.`);
11232
11242
  process.exitCode = 1;
11233
11243
  return;
11234
11244
  }
11235
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11245
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11236
11246
  const usage = await loadUsageIndex17(paths);
11237
11247
  const filterFn = ({ memory: mem }) => {
11238
11248
  if (mem.frontmatter.status !== "proposed" && mem.frontmatter.status !== "draft") return false;
@@ -11285,7 +11295,7 @@ function registerMemoryPending(memory2) {
11285
11295
  }
11286
11296
 
11287
11297
  // src/commands/memory-query.ts
11288
- import { existsSync as existsSync48 } from "fs";
11298
+ import { existsSync as existsSync47 } from "fs";
11289
11299
  import path30 from "path";
11290
11300
  import "commander";
11291
11301
  import {
@@ -11302,7 +11312,7 @@ function registerMemoryQuery(memory2) {
11302
11312
  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) => {
11303
11313
  const root = findProjectRoot25(opts.dir);
11304
11314
  const paths = resolveHaivePaths22(root);
11305
- if (!existsSync48(paths.memoriesDir)) {
11315
+ if (!existsSync47(paths.memoriesDir)) {
11306
11316
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
11307
11317
  process.exitCode = 1;
11308
11318
  return;
@@ -11313,7 +11323,7 @@ function registerMemoryQuery(memory2) {
11313
11323
  return;
11314
11324
  }
11315
11325
  const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
11316
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11326
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11317
11327
  const passesFilters2 = (mem) => {
11318
11328
  const fm = mem.frontmatter;
11319
11329
  if (opts.scope && fm.scope !== opts.scope) return false;
@@ -11360,8 +11370,8 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
11360
11370
  }
11361
11371
 
11362
11372
  // src/commands/memory-reject.ts
11363
- import { writeFile as writeFile24 } from "fs/promises";
11364
- import { existsSync as existsSync49 } from "fs";
11373
+ import { writeFile as writeFile23 } from "fs/promises";
11374
+ import { existsSync as existsSync48 } from "fs";
11365
11375
  import "commander";
11366
11376
  import {
11367
11377
  findProjectRoot as findProjectRoot26,
@@ -11375,19 +11385,19 @@ function registerMemoryReject(memory2) {
11375
11385
  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) => {
11376
11386
  const root = findProjectRoot26(opts.dir);
11377
11387
  const paths = resolveHaivePaths23(root);
11378
- if (!existsSync49(paths.memoriesDir)) {
11388
+ if (!existsSync48(paths.memoriesDir)) {
11379
11389
  ui.error(`No .ai/memories at ${root}.`);
11380
11390
  process.exitCode = 1;
11381
11391
  return;
11382
11392
  }
11383
- const memories = await loadMemoriesFromDir28(paths.memoriesDir);
11393
+ const memories = await loadMemoriesFromDir27(paths.memoriesDir);
11384
11394
  const loaded = memories.find((m) => m.memory.frontmatter.id === id);
11385
11395
  if (!loaded) {
11386
11396
  ui.error(`No memory with id "${id}".`);
11387
11397
  process.exitCode = 1;
11388
11398
  return;
11389
11399
  }
11390
- await writeFile24(
11400
+ await writeFile23(
11391
11401
  loaded.filePath,
11392
11402
  serializeMemory20({
11393
11403
  frontmatter: {
@@ -11411,7 +11421,7 @@ function registerMemoryReject(memory2) {
11411
11421
  }
11412
11422
 
11413
11423
  // src/commands/memory-rm.ts
11414
- import { existsSync as existsSync50 } from "fs";
11424
+ import { existsSync as existsSync49 } from "fs";
11415
11425
  import { unlink as unlink3 } from "fs/promises";
11416
11426
  import path31 from "path";
11417
11427
  import { createInterface as createInterface2 } from "readline/promises";
@@ -11426,12 +11436,12 @@ function registerMemoryRm(memory2) {
11426
11436
  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) => {
11427
11437
  const root = findProjectRoot27(opts.dir);
11428
11438
  const paths = resolveHaivePaths24(root);
11429
- if (!existsSync50(paths.memoriesDir)) {
11439
+ if (!existsSync49(paths.memoriesDir)) {
11430
11440
  ui.error(`No .ai/memories at ${root}.`);
11431
11441
  process.exitCode = 1;
11432
11442
  return;
11433
11443
  }
11434
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11444
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11435
11445
  const found = all.find((m) => m.memory.frontmatter.id === id);
11436
11446
  if (!found) {
11437
11447
  ui.error(`No memory with id "${id}".`);
@@ -11462,7 +11472,7 @@ function registerMemoryRm(memory2) {
11462
11472
  }
11463
11473
 
11464
11474
  // src/commands/memory-show.ts
11465
- import { existsSync as existsSync51 } from "fs";
11475
+ import { existsSync as existsSync50 } from "fs";
11466
11476
  import { readFile as readFile15 } from "fs/promises";
11467
11477
  import path33 from "path";
11468
11478
  import "commander";
@@ -11477,12 +11487,12 @@ function registerMemoryShow(memory2) {
11477
11487
  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) => {
11478
11488
  const root = findProjectRoot28(opts.dir);
11479
11489
  const paths = resolveHaivePaths25(root);
11480
- if (!existsSync51(paths.memoriesDir)) {
11490
+ if (!existsSync50(paths.memoriesDir)) {
11481
11491
  ui.error(`No .ai/memories at ${root}.`);
11482
11492
  process.exitCode = 1;
11483
11493
  return;
11484
11494
  }
11485
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11495
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11486
11496
  const found = all.find((m) => m.memory.frontmatter.id === id);
11487
11497
  if (!found) {
11488
11498
  ui.error(`No memory with id "${id}".`);
@@ -11521,7 +11531,7 @@ function registerMemoryShow(memory2) {
11521
11531
  }
11522
11532
 
11523
11533
  // src/commands/memory-stats.ts
11524
- import { existsSync as existsSync53 } from "fs";
11534
+ import { existsSync as existsSync51 } from "fs";
11525
11535
  import path34 from "path";
11526
11536
  import "commander";
11527
11537
  import {
@@ -11535,12 +11545,12 @@ function registerMemoryStats(memory2) {
11535
11545
  memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
11536
11546
  const root = findProjectRoot29(opts.dir);
11537
11547
  const paths = resolveHaivePaths26(root);
11538
- if (!existsSync53(paths.memoriesDir)) {
11548
+ if (!existsSync51(paths.memoriesDir)) {
11539
11549
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
11540
11550
  process.exitCode = 1;
11541
11551
  return;
11542
11552
  }
11543
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11553
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11544
11554
  const usage = await loadUsageIndex21(paths);
11545
11555
  const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
11546
11556
  if (target.length === 0) {
@@ -11566,7 +11576,7 @@ function registerMemoryStats(memory2) {
11566
11576
  }
11567
11577
 
11568
11578
  // src/commands/memory-impact.ts
11569
- import { existsSync as existsSync54 } from "fs";
11579
+ import { existsSync as existsSync53 } from "fs";
11570
11580
  import "commander";
11571
11581
  import {
11572
11582
  compareImpact,
@@ -11583,12 +11593,12 @@ function registerMemoryImpact(memory2) {
11583
11593
  ).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) => {
11584
11594
  const root = findProjectRoot30(opts.dir);
11585
11595
  const paths = resolveHaivePaths27(root);
11586
- if (!existsSync54(paths.memoriesDir)) {
11596
+ if (!existsSync53(paths.memoriesDir)) {
11587
11597
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
11588
11598
  process.exitCode = 1;
11589
11599
  return;
11590
11600
  }
11591
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11601
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11592
11602
  const usageIndex = await loadUsageIndex23(paths);
11593
11603
  let rows = all.filter((m) => !opts.id || m.memory.frontmatter.id === opts.id).map(({ memory: mem }) => {
11594
11604
  const fm = mem.frontmatter;
@@ -11654,8 +11664,8 @@ function pad(value, width) {
11654
11664
  }
11655
11665
 
11656
11666
  // src/commands/memory-feedback.ts
11657
- import { existsSync as existsSync55 } from "fs";
11658
- import { writeFile as writeFile25 } from "fs/promises";
11667
+ import { existsSync as existsSync54 } from "fs";
11668
+ import { writeFile as writeFile24 } from "fs/promises";
11659
11669
  import "commander";
11660
11670
  import {
11661
11671
  applyFeedbackAdjustment as applyFeedbackAdjustment2,
@@ -11681,12 +11691,12 @@ function registerMemoryFeedback(memory2) {
11681
11691
  }
11682
11692
  const root = findProjectRoot31(opts.dir);
11683
11693
  const paths = resolveHaivePaths28(root);
11684
- if (!existsSync55(paths.memoriesDir)) {
11694
+ if (!existsSync54(paths.memoriesDir)) {
11685
11695
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
11686
11696
  process.exitCode = 1;
11687
11697
  return;
11688
11698
  }
11689
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11699
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11690
11700
  const target = all.find((m) => m.memory.frontmatter.id === id);
11691
11701
  if (!target) {
11692
11702
  ui.error(`No memory with id '${id}'.`);
@@ -11703,7 +11713,7 @@ function registerMemoryFeedback(memory2) {
11703
11713
  const adjustedFrontmatter = applyFeedbackAdjustment2(target.memory.frontmatter, adjustment);
11704
11714
  if (adjustedFrontmatter !== target.memory.frontmatter) {
11705
11715
  target.memory.frontmatter = adjustedFrontmatter;
11706
- await writeFile25(target.filePath, serializeMemory21(target.memory), "utf8");
11716
+ await writeFile24(target.filePath, serializeMemory21(target.memory), "utf8");
11707
11717
  }
11708
11718
  const impact = computeImpact4(target.memory.frontmatter, usage);
11709
11719
  if (opts.json) {
@@ -11721,8 +11731,8 @@ function registerMemoryFeedback(memory2) {
11721
11731
  }
11722
11732
 
11723
11733
  // src/commands/memory-verify.ts
11724
- import { writeFile as writeFile26 } from "fs/promises";
11725
- import { existsSync as existsSync56 } from "fs";
11734
+ import { writeFile as writeFile25 } from "fs/promises";
11735
+ import { existsSync as existsSync55 } from "fs";
11726
11736
  import path35 from "path";
11727
11737
  import "commander";
11728
11738
  import {
@@ -11737,7 +11747,7 @@ function registerMemoryVerify(memory2) {
11737
11747
  ).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) => {
11738
11748
  const root = findProjectRoot32(opts.dir);
11739
11749
  const paths = resolveHaivePaths29(root);
11740
- if (!existsSync56(paths.memoriesDir)) {
11750
+ if (!existsSync55(paths.memoriesDir)) {
11741
11751
  if (opts.json) {
11742
11752
  console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
11743
11753
  } else {
@@ -11746,7 +11756,7 @@ function registerMemoryVerify(memory2) {
11746
11756
  process.exitCode = 1;
11747
11757
  return;
11748
11758
  }
11749
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11759
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
11750
11760
  const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
11751
11761
  if (opts.id && targets.length === 0) {
11752
11762
  if (opts.json) {
@@ -11795,7 +11805,7 @@ function registerMemoryVerify(memory2) {
11795
11805
  }
11796
11806
  if (opts.update) {
11797
11807
  const next = applyVerification2(mem, result);
11798
- await writeFile26(filePath, serializeMemory23(next), "utf8");
11808
+ await writeFile25(filePath, serializeMemory23(next), "utf8");
11799
11809
  updated++;
11800
11810
  }
11801
11811
  }
@@ -11858,7 +11868,7 @@ function applyVerification2(mem, result) {
11858
11868
 
11859
11869
  // src/commands/memory-import.ts
11860
11870
  import { readFile as readFile16 } from "fs/promises";
11861
- import { existsSync as existsSync57 } from "fs";
11871
+ import { existsSync as existsSync56 } from "fs";
11862
11872
  import "commander";
11863
11873
  import {
11864
11874
  findProjectRoot as findProjectRoot33,
@@ -11870,12 +11880,12 @@ function registerMemoryImport(memory2) {
11870
11880
  ).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
11871
11881
  const root = findProjectRoot33(opts.dir);
11872
11882
  const paths = resolveHaivePaths30(root);
11873
- if (!existsSync57(paths.haiveDir)) {
11883
+ if (!existsSync56(paths.haiveDir)) {
11874
11884
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11875
11885
  process.exitCode = 1;
11876
11886
  return;
11877
11887
  }
11878
- if (!existsSync57(opts.from)) {
11888
+ if (!existsSync56(opts.from)) {
11879
11889
  ui.error(`File not found: ${opts.from}`);
11880
11890
  process.exitCode = 1;
11881
11891
  return;
@@ -11908,8 +11918,8 @@ function registerMemoryImport(memory2) {
11908
11918
  }
11909
11919
 
11910
11920
  // src/commands/memory-import-changelog.ts
11911
- import { existsSync as existsSync58 } from "fs";
11912
- import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile27 } from "fs/promises";
11921
+ import { existsSync as existsSync57 } from "fs";
11922
+ import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile26 } from "fs/promises";
11913
11923
  import path36 from "path";
11914
11924
  import "commander";
11915
11925
  import {
@@ -11983,7 +11993,7 @@ function registerMemoryImportChangelog(memory2) {
11983
11993
  const root = findProjectRoot34(opts.dir);
11984
11994
  const paths = resolveHaivePaths31(root);
11985
11995
  const changelogPath = path36.resolve(root, opts.fromChangelog);
11986
- if (!existsSync58(changelogPath)) {
11996
+ if (!existsSync57(changelogPath)) {
11987
11997
  ui.error(`CHANGELOG not found: ${changelogPath}`);
11988
11998
  process.exitCode = 1;
11989
11999
  return;
@@ -12046,7 +12056,7 @@ function registerMemoryImportChangelog(memory2) {
12046
12056
  paths: [path36.relative(root, changelogPath)],
12047
12057
  topic: `changelog-${pkgName}-${entry.version}`
12048
12058
  });
12049
- await writeFile27(
12059
+ await writeFile26(
12050
12060
  path36.join(teamDir, `${fm.id}.md`),
12051
12061
  serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
12052
12062
  "utf8"
@@ -12070,15 +12080,15 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
12070
12080
  }
12071
12081
 
12072
12082
  // src/commands/memory-digest.ts
12073
- import { existsSync as existsSync59 } from "fs";
12074
- import { writeFile as writeFile28 } from "fs/promises";
12083
+ import { existsSync as existsSync58 } from "fs";
12084
+ import { writeFile as writeFile27 } from "fs/promises";
12075
12085
  import path37 from "path";
12076
12086
  import "commander";
12077
12087
  import {
12078
12088
  deriveConfidence as deriveConfidence12,
12079
12089
  findProjectRoot as findProjectRoot35,
12080
12090
  getUsage as getUsage20,
12081
- loadMemoriesFromDir as loadMemoriesFromDir29,
12091
+ loadMemoriesFromDir as loadMemoriesFromDir28,
12082
12092
  loadUsageIndex as loadUsageIndex25,
12083
12093
  resolveHaivePaths as resolveHaivePaths32
12084
12094
  } from "@hiveai/core";
@@ -12095,7 +12105,7 @@ function registerMemoryDigest(program2) {
12095
12105
  ).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) => {
12096
12106
  const root = findProjectRoot35(opts.dir);
12097
12107
  const paths = resolveHaivePaths32(root);
12098
- if (!existsSync59(paths.memoriesDir)) {
12108
+ if (!existsSync58(paths.memoriesDir)) {
12099
12109
  ui.error("No .ai/memories found. Run `haive init` first.");
12100
12110
  process.exitCode = 1;
12101
12111
  return;
@@ -12103,7 +12113,7 @@ function registerMemoryDigest(program2) {
12103
12113
  const days = Math.max(1, Number(opts.days ?? 7));
12104
12114
  const scopeFilter = opts.scope ?? "team";
12105
12115
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
12106
- const all = await loadMemoriesFromDir29(paths.memoriesDir);
12116
+ const all = await loadMemoriesFromDir28(paths.memoriesDir);
12107
12117
  const usage = await loadUsageIndex25(paths);
12108
12118
  const recent = all.filter(({ memory: mem }) => {
12109
12119
  const fm = mem.frontmatter;
@@ -12168,7 +12178,7 @@ function registerMemoryDigest(program2) {
12168
12178
  const digest = lines.join("\n");
12169
12179
  if (opts.out) {
12170
12180
  const outPath = path37.resolve(process.cwd(), opts.out);
12171
- await writeFile28(outPath, digest, "utf8");
12181
+ await writeFile27(outPath, digest, "utf8");
12172
12182
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
12173
12183
  } else {
12174
12184
  console.log(digest);
@@ -12177,15 +12187,15 @@ function registerMemoryDigest(program2) {
12177
12187
  }
12178
12188
 
12179
12189
  // src/commands/session-end.ts
12180
- import { writeFile as writeFile29, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
12181
- import { existsSync as existsSync60 } from "fs";
12190
+ import { writeFile as writeFile28, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
12191
+ import { existsSync as existsSync59 } from "fs";
12182
12192
  import { spawn as spawn4 } from "child_process";
12183
12193
  import path38 from "path";
12184
12194
  import "commander";
12185
12195
  import {
12186
12196
  buildFrontmatter as buildFrontmatter10,
12187
12197
  findProjectRoot as findProjectRoot36,
12188
- loadMemoriesFromDir as loadMemoriesFromDir30,
12198
+ loadMemoriesFromDir as loadMemoriesFromDir29,
12189
12199
  loadPreventionEvents as loadPreventionEvents2,
12190
12200
  loadUsageIndex as loadUsageIndex26,
12191
12201
  memoryFilePath as memoryFilePath10,
@@ -12196,7 +12206,7 @@ import {
12196
12206
  } from "@hiveai/core";
12197
12207
  async function buildAutoRecap(paths) {
12198
12208
  const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12199
- if (!existsSync60(obsFile)) return await buildGitAutoRecap(paths);
12209
+ if (!existsSync59(obsFile)) return await buildGitAutoRecap(paths);
12200
12210
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12201
12211
  if (!raw.trim()) return await buildGitAutoRecap(paths);
12202
12212
  const lines = raw.split("\n").filter(Boolean);
@@ -12365,7 +12375,7 @@ function runGit(cwd, args) {
12365
12375
  }
12366
12376
  async function observationStart(paths) {
12367
12377
  const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12368
- if (!existsSync60(obsFile)) return null;
12378
+ if (!existsSync59(obsFile)) return null;
12369
12379
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12370
12380
  let first = null;
12371
12381
  for (const line of raw.split("\n")) {
@@ -12381,7 +12391,7 @@ async function observationStart(paths) {
12381
12391
  }
12382
12392
  async function printCaughtForYou(paths, since, quiet) {
12383
12393
  if (quiet) return;
12384
- const memories = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
12394
+ const memories = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir29(paths.memoriesDir) : [];
12385
12395
  const usage = await loadUsageIndex26(paths);
12386
12396
  const events = await loadPreventionEvents2(paths);
12387
12397
  const summary = summarizeCaughtForYou(events, memories, usage, {
@@ -12419,7 +12429,7 @@ function registerSessionEnd(session2) {
12419
12429
  ).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").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) => {
12420
12430
  const root = findProjectRoot36(opts.dir);
12421
12431
  const paths = resolveHaivePaths33(root);
12422
- if (!existsSync60(paths.haiveDir)) {
12432
+ if (!existsSync59(paths.haiveDir)) {
12423
12433
  if (opts.auto || opts.quiet) return;
12424
12434
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
12425
12435
  process.exitCode = 1;
@@ -12453,7 +12463,7 @@ function registerSessionEnd(session2) {
12453
12463
  });
12454
12464
  const topic = recapTopic2(scope, opts.module);
12455
12465
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
12456
- const missingPaths = filesTouched.filter((p) => !existsSync60(path38.resolve(root, p)));
12466
+ const missingPaths = filesTouched.filter((p) => !existsSync59(path38.resolve(root, p)));
12457
12467
  if (missingPaths.length > 0 && !opts.quiet) {
12458
12468
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
12459
12469
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
@@ -12461,11 +12471,11 @@ function registerSessionEnd(session2) {
12461
12471
  const cleanupObservations = async () => {
12462
12472
  if (!opts.auto) return;
12463
12473
  const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12464
- if (existsSync60(obsFile)) await rm2(obsFile).catch(() => {
12474
+ if (existsSync59(obsFile)) await rm2(obsFile).catch(() => {
12465
12475
  });
12466
12476
  };
12467
- if (existsSync60(paths.memoriesDir)) {
12468
- const existing = await loadMemoriesFromDir30(paths.memoriesDir);
12477
+ if (existsSync59(paths.memoriesDir)) {
12478
+ const existing = await loadMemoriesFromDir29(paths.memoriesDir);
12469
12479
  const topicMatch = existing.find(
12470
12480
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
12471
12481
  );
@@ -12481,7 +12491,7 @@ function registerSessionEnd(session2) {
12481
12491
  paths: filesTouched.length ? filesTouched : fm.anchor.paths
12482
12492
  }
12483
12493
  };
12484
- await writeFile29(topicMatch.filePath, serializeMemory25({ frontmatter: newFrontmatter, body }), "utf8");
12494
+ await writeFile28(topicMatch.filePath, serializeMemory25({ frontmatter: newFrontmatter, body }), "utf8");
12485
12495
  await cleanupObservations();
12486
12496
  if (!opts.quiet) {
12487
12497
  ui.success(`Session recap updated (revision #${revisionCount})`);
@@ -12504,7 +12514,7 @@ function registerSessionEnd(session2) {
12504
12514
  });
12505
12515
  const file = memoryFilePath10(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
12506
12516
  await mkdir16(path38.dirname(file), { recursive: true });
12507
- await writeFile29(file, serializeMemory25({ frontmatter, body }), "utf8");
12517
+ await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
12508
12518
  await cleanupObservations();
12509
12519
  if (!opts.quiet) {
12510
12520
  ui.success(`Session recap created`);
@@ -12528,7 +12538,7 @@ function normalizeAnchorPath(root, filePath) {
12528
12538
  }
12529
12539
 
12530
12540
  // src/commands/snapshot.ts
12531
- import { existsSync as existsSync61 } from "fs";
12541
+ import { existsSync as existsSync60 } from "fs";
12532
12542
  import { readdir as readdir4 } from "fs/promises";
12533
12543
  import path39 from "path";
12534
12544
  import "commander";
@@ -12563,14 +12573,14 @@ function registerSnapshot(program2) {
12563
12573
  ).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
12564
12574
  const root = findProjectRoot37(opts.dir);
12565
12575
  const paths = resolveHaivePaths34(root);
12566
- if (!existsSync61(paths.haiveDir)) {
12576
+ if (!existsSync60(paths.haiveDir)) {
12567
12577
  ui.error("No .ai/ found. Run `haive init` first.");
12568
12578
  process.exitCode = 1;
12569
12579
  return;
12570
12580
  }
12571
12581
  if (opts.list) {
12572
12582
  const contractsDir = path39.join(paths.haiveDir, "contracts");
12573
- if (!existsSync61(contractsDir)) {
12583
+ if (!existsSync60(contractsDir)) {
12574
12584
  console.log(ui.dim("No contract snapshots found."));
12575
12585
  return;
12576
12586
  }
@@ -12694,15 +12704,15 @@ function detectFormat(filePath) {
12694
12704
  }
12695
12705
 
12696
12706
  // src/commands/hub.ts
12697
- import { existsSync as existsSync63 } from "fs";
12698
- import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile30, copyFile } from "fs/promises";
12707
+ import { existsSync as existsSync61 } from "fs";
12708
+ import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
12699
12709
  import path40 from "path";
12700
12710
  import { spawnSync as spawnSync5 } from "child_process";
12701
12711
  import "commander";
12702
12712
  import {
12703
12713
  findProjectRoot as findProjectRoot38,
12704
12714
  loadConfig as loadConfig8,
12705
- loadMemoriesFromDir as loadMemoriesFromDir31,
12715
+ loadMemoriesFromDir as loadMemoriesFromDir30,
12706
12716
  resolveHaivePaths as resolveHaivePaths35,
12707
12717
  saveConfig as saveConfig3,
12708
12718
  serializeMemory as serializeMemory26
@@ -12728,7 +12738,7 @@ function registerHub(program2) {
12728
12738
  }
12729
12739
  const sharedDir = path40.join(absPath, ".ai", "memories", "shared");
12730
12740
  await mkdir17(sharedDir, { recursive: true });
12731
- await writeFile30(
12741
+ await writeFile29(
12732
12742
  path40.join(absPath, ".ai", "README.md"),
12733
12743
  `# hAIve Team Knowledge Hub
12734
12744
 
@@ -12750,7 +12760,7 @@ haive hub pull # import into a project
12750
12760
  `,
12751
12761
  "utf8"
12752
12762
  );
12753
- await writeFile30(
12763
+ await writeFile29(
12754
12764
  path40.join(absPath, ".gitignore"),
12755
12765
  ".ai/.cache/\n.ai/memories/personal/\n",
12756
12766
  "utf8"
@@ -12796,7 +12806,7 @@ Next steps:
12796
12806
  return;
12797
12807
  }
12798
12808
  const hubRoot = path40.resolve(root, config.hubPath);
12799
- if (!existsSync63(hubRoot)) {
12809
+ if (!existsSync61(hubRoot)) {
12800
12810
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
12801
12811
  process.exitCode = 1;
12802
12812
  return;
@@ -12804,7 +12814,7 @@ Next steps:
12804
12814
  const projectName = path40.basename(root);
12805
12815
  const destDir = path40.join(hubRoot, ".ai", "memories", "shared", projectName);
12806
12816
  await mkdir17(destDir, { recursive: true });
12807
- const all = await loadMemoriesFromDir31(paths.memoriesDir);
12817
+ const all = await loadMemoriesFromDir30(paths.memoriesDir);
12808
12818
  const shared = all.filter(
12809
12819
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
12810
12820
  !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
@@ -12822,7 +12832,7 @@ Next steps:
12822
12832
  const fm = memory2.frontmatter;
12823
12833
  const fileName = `${fm.id}.md`;
12824
12834
  const destPath = path40.join(destDir, fileName);
12825
- await writeFile30(destPath, serializeMemory26(memory2), "utf8");
12835
+ await writeFile29(destPath, serializeMemory26(memory2), "utf8");
12826
12836
  pushed++;
12827
12837
  }
12828
12838
  console.log(ui.green(`\u2713 Pushed ${pushed} shared memor${pushed === 1 ? "y" : "ies"} to hub`));
@@ -12866,7 +12876,7 @@ Next steps:
12866
12876
  }
12867
12877
  const hubRoot = path40.resolve(root, config.hubPath);
12868
12878
  const hubSharedDir = path40.join(hubRoot, ".ai", "memories", "shared");
12869
- if (!existsSync63(hubSharedDir)) {
12879
+ if (!existsSync61(hubSharedDir)) {
12870
12880
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
12871
12881
  return;
12872
12882
  }
@@ -12921,7 +12931,7 @@ Next steps:
12921
12931
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
12922
12932
  );
12923
12933
  const sharedDir = path40.join(paths.memoriesDir, "shared");
12924
- if (existsSync63(sharedDir)) {
12934
+ if (existsSync61(sharedDir)) {
12925
12935
  const { readdir: readdir7 } = await import("fs/promises");
12926
12936
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
12927
12937
  console.log(`
@@ -12933,7 +12943,7 @@ Next steps:
12933
12943
  } else {
12934
12944
  console.log(ui.dim(" No imported shared memories yet."));
12935
12945
  }
12936
- const all = await loadMemoriesFromDir31(paths.memoriesDir);
12946
+ const all = await loadMemoriesFromDir30(paths.memoriesDir);
12937
12947
  const outgoing = all.filter(
12938
12948
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
12939
12949
  );
@@ -12943,20 +12953,20 @@ Next steps:
12943
12953
  console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
12944
12954
  }
12945
12955
  void readFile19;
12946
- void writeFile30;
12956
+ void writeFile29;
12947
12957
  void saveConfig3;
12948
12958
  });
12949
12959
  }
12950
12960
 
12951
12961
  // src/commands/stats.ts
12952
12962
  import "commander";
12953
- import { existsSync as existsSync64 } from "fs";
12954
- import { mkdir as mkdir18, writeFile as writeFile31 } from "fs/promises";
12963
+ import { existsSync as existsSync63 } from "fs";
12964
+ import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
12955
12965
  import path41 from "path";
12956
12966
  import {
12957
12967
  aggregateUsage,
12958
12968
  findProjectRoot as findProjectRoot39,
12959
- loadMemoriesFromDir as loadMemoriesFromDir33,
12969
+ loadMemoriesFromDir as loadMemoriesFromDir31,
12960
12970
  loadUsageIndex as loadUsageIndex27,
12961
12971
  parseSince,
12962
12972
  readUsageEvents as readUsageEvents2,
@@ -13028,8 +13038,8 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13028
13038
  const size = await usageLogSize(paths);
13029
13039
  let events = await readUsageEvents2(paths);
13030
13040
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
13031
- if (existsSync64(paths.memoriesDir)) {
13032
- const mems = await loadMemoriesFromDir33(paths.memoriesDir);
13041
+ if (existsSync63(paths.memoriesDir)) {
13042
+ const mems = await loadMemoriesFromDir31(paths.memoriesDir);
13033
13043
  for (const { memory: memory2 } of mems) {
13034
13044
  const fm = memory2.frontmatter;
13035
13045
  if (fm.type === "session_recap") memoryCount.total_skipped_session++;
@@ -13071,7 +13081,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13071
13081
  top_memory_reads: memoryHitsLeader,
13072
13082
  roi_hints: roiHints
13073
13083
  };
13074
- await writeFile31(outAbs, JSON.stringify(payload, null, 2), "utf8");
13084
+ await writeFile30(outAbs, JSON.stringify(payload, null, 2), "utf8");
13075
13085
  ui.success(`Wrote ROI / usage rollup \u2192 ${outAbs}`);
13076
13086
  }
13077
13087
  async function renderMemoryHits(paths, opts) {
@@ -13248,8 +13258,8 @@ function summarize(name, t0, payload, notes) {
13248
13258
  }
13249
13259
 
13250
13260
  // src/commands/benchmark.ts
13251
- import { existsSync as existsSync65 } from "fs";
13252
- import { readdir as readdir5, readFile as readFile20, writeFile as writeFile33 } from "fs/promises";
13261
+ import { existsSync as existsSync64 } from "fs";
13262
+ import { readdir as readdir5, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
13253
13263
  import path43 from "path";
13254
13264
  import "commander";
13255
13265
  import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hiveai/core";
@@ -13266,7 +13276,7 @@ function registerBenchmark(program2) {
13266
13276
  const markdown = renderMarkdown(root, summary, rows);
13267
13277
  if (opts.out) {
13268
13278
  const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
13269
- await writeFile33(outFile, markdown, "utf8");
13279
+ await writeFile31(outFile, markdown, "utf8");
13270
13280
  ui.success(`wrote ${path43.relative(process.cwd(), outFile)}`);
13271
13281
  return;
13272
13282
  }
@@ -13296,14 +13306,14 @@ function resolveBenchmarkRoot(dir) {
13296
13306
  return path43.join(projectRoot, candidate);
13297
13307
  }
13298
13308
  async function collectRows(root) {
13299
- if (!existsSync65(root)) throw new Error(`Benchmark directory not found: ${root}`);
13309
+ if (!existsSync64(root)) throw new Error(`Benchmark directory not found: ${root}`);
13300
13310
  const entries = await readdir5(root, { withFileTypes: true });
13301
13311
  const rows = [];
13302
13312
  for (const entry of entries) {
13303
13313
  if (!entry.isDirectory()) continue;
13304
13314
  const fixtureDir = path43.join(root, entry.name);
13305
13315
  const reportFile = path43.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
13306
- if (!existsSync65(reportFile)) continue;
13316
+ if (!existsSync64(reportFile)) continue;
13307
13317
  const report = await readFile20(reportFile, "utf8");
13308
13318
  rows.push(parseAgentReport(entry.name, report));
13309
13319
  }
@@ -13393,8 +13403,8 @@ function escapeRegExp(value) {
13393
13403
  }
13394
13404
 
13395
13405
  // src/commands/eval.ts
13396
- import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile34 } from "fs/promises";
13397
- import { existsSync as existsSync66 } from "fs";
13406
+ import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
13407
+ import { existsSync as existsSync65 } from "fs";
13398
13408
  import path44 from "path";
13399
13409
  import "commander";
13400
13410
  import {
@@ -13422,7 +13432,7 @@ function registerEval(program2) {
13422
13432
  ).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) => {
13423
13433
  const root = findProjectRoot42(opts.dir);
13424
13434
  const paths = resolveHaivePaths38(root);
13425
- if (!existsSync66(paths.memoriesDir)) {
13435
+ if (!existsSync65(paths.memoriesDir)) {
13426
13436
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
13427
13437
  process.exitCode = 1;
13428
13438
  return;
@@ -13501,13 +13511,13 @@ function registerEval(program2) {
13501
13511
  gate_precision: gatePrecision
13502
13512
  };
13503
13513
  await mkdir19(path44.dirname(baselineFile), { recursive: true });
13504
- await writeFile34(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
13514
+ await writeFile33(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
13505
13515
  if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path44.relative(root, baselineFile)}`);
13506
13516
  }
13507
13517
  let delta = null;
13508
13518
  let gateDelta = null;
13509
13519
  if (opts.compare || opts.regressionGate) {
13510
- if (!existsSync66(baselineFile)) {
13520
+ if (!existsSync65(baselineFile)) {
13511
13521
  if (opts.regressionGate) {
13512
13522
  if (!opts.json) ui.info(`No baseline at ${path44.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
13513
13523
  } else {
@@ -13545,7 +13555,7 @@ function registerEval(program2) {
13545
13555
  const md = renderMarkdown2(root, k, resolvedSpec.source, report, gatePrecision);
13546
13556
  if (opts.out) {
13547
13557
  const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
13548
- await writeFile34(outFile, md, "utf8");
13558
+ await writeFile33(outFile, md, "utf8");
13549
13559
  ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
13550
13560
  } else {
13551
13561
  console.log(md);
@@ -13632,10 +13642,10 @@ async function resolveSpec(opts, root, memoriesDir) {
13632
13642
  return { spec: JSON.parse(raw), source: file };
13633
13643
  }
13634
13644
  const defaultSpec = path44.join(root, ".ai", "eval", "spec.json");
13635
- if (existsSync66(defaultSpec)) {
13645
+ if (existsSync65(defaultSpec)) {
13636
13646
  const raw = await readFile21(defaultSpec, "utf8");
13637
13647
  const explicit = JSON.parse(raw);
13638
- const memories2 = await loadMemoriesFromDir28(memoriesDir);
13648
+ const memories2 = await loadMemoriesFromDir27(memoriesDir);
13639
13649
  const synthesized = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
13640
13650
  return {
13641
13651
  spec: {
@@ -13645,7 +13655,7 @@ async function resolveSpec(opts, root, memoriesDir) {
13645
13655
  source: ".ai/eval/spec.json + synthesized anchored retrieval"
13646
13656
  };
13647
13657
  }
13648
- const memories = await loadMemoriesFromDir28(memoriesDir);
13658
+ const memories = await loadMemoriesFromDir27(memoriesDir);
13649
13659
  return {
13650
13660
  spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
13651
13661
  source: "synthesized anchored retrieval"
@@ -13743,8 +13753,8 @@ function renderMarkdown2(root, k, source, report, gatePrecision) {
13743
13753
  }
13744
13754
 
13745
13755
  // src/commands/memory-suggest.ts
13746
- import { mkdir as mkdir20, writeFile as writeFile35 } from "fs/promises";
13747
- import { existsSync as existsSync67 } from "fs";
13756
+ import { mkdir as mkdir20, writeFile as writeFile34 } from "fs/promises";
13757
+ import { existsSync as existsSync66 } from "fs";
13748
13758
  import path45 from "path";
13749
13759
  import "commander";
13750
13760
  import {
@@ -13752,7 +13762,7 @@ import {
13752
13762
  buildFrontmatter as buildFrontmatter11,
13753
13763
  findProjectRoot as findProjectRoot43,
13754
13764
  loadConfig as loadConfig10,
13755
- loadMemoriesFromDir as loadMemoriesFromDir34,
13765
+ loadMemoriesFromDir as loadMemoriesFromDir33,
13756
13766
  memoryFilePath as memoryFilePath11,
13757
13767
  parseSince as parseSince2,
13758
13768
  readUsageEvents as readUsageEvents3,
@@ -13823,7 +13833,7 @@ function registerMemorySuggest(memory2) {
13823
13833
  }
13824
13834
  const created = [];
13825
13835
  const skipped = [];
13826
- const existing = existsSync67(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
13836
+ const existing = existsSync66(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
13827
13837
  for (const s of top) {
13828
13838
  const slug = slugify2(s.query);
13829
13839
  if (!slug) {
@@ -13847,11 +13857,11 @@ function registerMemorySuggest(memory2) {
13847
13857
  const body = renderTemplate(s, fm.id, status);
13848
13858
  const file = memoryFilePath11(paths, fm.scope, fm.id, fm.module);
13849
13859
  await mkdir20(path45.dirname(file), { recursive: true });
13850
- if (existsSync67(file)) {
13860
+ if (existsSync66(file)) {
13851
13861
  skipped.push({ query: s.query, reason: `file already exists at ${path45.relative(root, file)}` });
13852
13862
  continue;
13853
13863
  }
13854
- await writeFile35(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
13864
+ await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
13855
13865
  created.push({ id: fm.id, file: path45.relative(root, file), query: s.query });
13856
13866
  }
13857
13867
  if (opts.json) {
@@ -13950,8 +13960,8 @@ function truncate2(text, max) {
13950
13960
  }
13951
13961
 
13952
13962
  // src/commands/memory-archive.ts
13953
- import { existsSync as existsSync68 } from "fs";
13954
- import { writeFile as writeFile36 } from "fs/promises";
13963
+ import { existsSync as existsSync67 } from "fs";
13964
+ import { writeFile as writeFile35 } from "fs/promises";
13955
13965
  import path46 from "path";
13956
13966
  import "commander";
13957
13967
  import {
@@ -13959,7 +13969,7 @@ import {
13959
13969
  getUsage as getUsage21,
13960
13970
  retirementSignal as retirementSignal2,
13961
13971
  loadConfig as loadConfig11,
13962
- loadMemoriesFromDir as loadMemoriesFromDir35,
13972
+ loadMemoriesFromDir as loadMemoriesFromDir34,
13963
13973
  loadUsageIndex as loadUsageIndex29,
13964
13974
  resolveHaivePaths as resolveHaivePaths40,
13965
13975
  serializeMemory as serializeMemory28
@@ -13971,7 +13981,7 @@ function registerMemoryArchive(memory2) {
13971
13981
  ).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) => {
13972
13982
  const root = findProjectRoot44(opts.dir);
13973
13983
  const paths = resolveHaivePaths40(root);
13974
- if (!existsSync68(paths.memoriesDir)) {
13984
+ if (!existsSync67(paths.memoriesDir)) {
13975
13985
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
13976
13986
  process.exitCode = 1;
13977
13987
  return;
@@ -13985,7 +13995,7 @@ function registerMemoryArchive(memory2) {
13985
13995
  return;
13986
13996
  }
13987
13997
  const cutoff = Date.now() - minDays * MS_PER_DAY2;
13988
- const all = await loadMemoriesFromDir35(paths.memoriesDir);
13998
+ const all = await loadMemoriesFromDir34(paths.memoriesDir);
13989
13999
  const usage = await loadUsageIndex29(paths);
13990
14000
  const typeFilter = opts.type === "all" ? null : opts.type ?? "attempt";
13991
14001
  const candidates = [];
@@ -13996,7 +14006,7 @@ function registerMemoryArchive(memory2) {
13996
14006
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
13997
14007
  const retired = retirementSignal2(fm, mem.body);
13998
14008
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
13999
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync68(path46.join(paths.root, p)));
14009
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync67(path46.join(paths.root, p)));
14000
14010
  const isAnchorless = !hasAnyAnchor;
14001
14011
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
14002
14012
  const u = getUsage21(usage, fm.id);
@@ -14045,7 +14055,7 @@ function registerMemoryArchive(memory2) {
14045
14055
  if (!found) continue;
14046
14056
  const fm = { ...found.memory.frontmatter, status: "deprecated" };
14047
14057
  try {
14048
- await writeFile36(c.filePath, serializeMemory28({ frontmatter: fm, body: found.memory.body }), "utf8");
14058
+ await writeFile35(c.filePath, serializeMemory28({ frontmatter: fm, body: found.memory.body }), "utf8");
14049
14059
  archived++;
14050
14060
  } catch (err) {
14051
14061
  if (!opts.json) {
@@ -14071,8 +14081,8 @@ function parseDays(input) {
14071
14081
  }
14072
14082
 
14073
14083
  // src/commands/doctor.ts
14074
- import { existsSync as existsSync69, statSync as statSync2 } from "fs";
14075
- import { readFile as readFile23, stat, writeFile as writeFile37 } from "fs/promises";
14084
+ import { existsSync as existsSync68, statSync as statSync2 } from "fs";
14085
+ import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
14076
14086
  import path47 from "path";
14077
14087
  import { execFileSync, execSync as execSync3 } from "child_process";
14078
14088
  import "commander";
@@ -14083,7 +14093,7 @@ import {
14083
14093
  isStackPackSeed as isStackPackSeed2,
14084
14094
  loadCodeMap as loadCodeMap7,
14085
14095
  loadConfig as loadConfig12,
14086
- loadMemoriesFromDir as loadMemoriesFromDir36,
14096
+ loadMemoriesFromDir as loadMemoriesFromDir35,
14087
14097
  loadUsageIndex as loadUsageIndex30,
14088
14098
  readUsageEvents as readUsageEvents4,
14089
14099
  resolveHaivePaths as resolveHaivePaths41
@@ -14098,7 +14108,7 @@ function registerDoctor(program2) {
14098
14108
  const findings = [];
14099
14109
  const repairs = [];
14100
14110
  const config = await loadConfig12(paths);
14101
- if (!existsSync69(paths.haiveDir)) {
14111
+ if (!existsSync68(paths.haiveDir)) {
14102
14112
  findings.push({
14103
14113
  severity: "error",
14104
14114
  code: "not-initialized",
@@ -14119,7 +14129,7 @@ function registerDoctor(program2) {
14119
14129
  })
14120
14130
  );
14121
14131
  }
14122
- if (!existsSync69(paths.projectContext)) {
14132
+ if (!existsSync68(paths.projectContext)) {
14123
14133
  findings.push({
14124
14134
  severity: "warn",
14125
14135
  code: "no-project-context",
@@ -14148,7 +14158,7 @@ function registerDoctor(program2) {
14148
14158
  });
14149
14159
  }
14150
14160
  }
14151
- const memories = existsSync69(paths.memoriesDir) ? await loadMemoriesFromDir36(paths.memoriesDir) : [];
14161
+ const memories = existsSync68(paths.memoriesDir) ? await loadMemoriesFromDir35(paths.memoriesDir) : [];
14152
14162
  const now = Date.now();
14153
14163
  if (memories.length === 0) {
14154
14164
  findings.push({
@@ -14300,7 +14310,7 @@ function registerDoctor(program2) {
14300
14310
  if (config.enforcement?.requireBriefingFirst) {
14301
14311
  const claudeSettings = path47.join(root, ".claude", "settings.local.json");
14302
14312
  let hasClaudeEnforcement = false;
14303
- if (existsSync69(claudeSettings)) {
14313
+ if (existsSync68(claudeSettings)) {
14304
14314
  try {
14305
14315
  const { readFile: readFile28 } = await import("fs/promises");
14306
14316
  const raw = await readFile28(claudeSettings, "utf8");
@@ -14326,7 +14336,7 @@ function registerDoctor(program2) {
14326
14336
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
14327
14337
  });
14328
14338
  }
14329
- findings.push(...await collectInstallFindings(root, "0.18.0"));
14339
+ findings.push(...await collectInstallFindings(root, "0.20.0"));
14330
14340
  findings.push(...await collectToolchainFindings(root));
14331
14341
  try {
14332
14342
  const legacyRaw = execSync3("haive-mcp --version", {
@@ -14334,7 +14344,7 @@ function registerDoctor(program2) {
14334
14344
  timeout: 3e3,
14335
14345
  stdio: ["ignore", "pipe", "ignore"]
14336
14346
  }).trim();
14337
- const cliVersion = "0.18.0";
14347
+ const cliVersion = "0.20.0";
14338
14348
  if (legacyRaw && legacyRaw !== cliVersion) {
14339
14349
  findings.push({
14340
14350
  severity: "warn",
@@ -14356,14 +14366,14 @@ npm uninstall -g @hiveai/mcp`
14356
14366
  ];
14357
14367
  const staleConfigs = [];
14358
14368
  for (const cfgPath of configPaths) {
14359
- if (!existsSync69(cfgPath)) continue;
14369
+ if (!existsSync68(cfgPath)) continue;
14360
14370
  try {
14361
14371
  const raw = await readFile23(cfgPath, "utf8");
14362
14372
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
14363
14373
  staleConfigs.push(path47.relative(root, cfgPath));
14364
14374
  if (opts.fix && !opts.dryRun) {
14365
14375
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
14366
- await writeFile37(cfgPath, updated, "utf8");
14376
+ await writeFile36(cfgPath, updated, "utf8");
14367
14377
  }
14368
14378
  }
14369
14379
  } catch {
@@ -14652,7 +14662,7 @@ which -a haive`
14652
14662
  ];
14653
14663
  for (const rel of integrationFiles) {
14654
14664
  const file = path47.join(root, rel);
14655
- if (!existsSync69(file)) continue;
14665
+ if (!existsSync68(file)) continue;
14656
14666
  const text = await readFile23(file, "utf8").catch(() => "");
14657
14667
  for (const bin of extractAbsoluteHaiveBins(text)) {
14658
14668
  const version = versionForBinary(bin);
@@ -14700,7 +14710,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14700
14710
  const isHaiveWorkspace = (await readJson(path47.join(root, "package.json")))?.name === "haive-monorepo";
14701
14711
  if (!isHaiveWorkspace) return findings;
14702
14712
  const cliDist = path47.join(root, "packages/cli/dist/index.js");
14703
- if (!existsSync69(cliDist)) {
14713
+ if (!existsSync68(cliDist)) {
14704
14714
  findings.push({
14705
14715
  severity: "warn",
14706
14716
  code: "workspace-dist-missing",
@@ -14724,7 +14734,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14724
14734
  "packages/core/src/index.ts",
14725
14735
  "packages/mcp/src/server.ts",
14726
14736
  "packages/cli/src/index.ts"
14727
- ].map((rel) => path47.join(root, rel)).filter(existsSync69);
14737
+ ].map((rel) => path47.join(root, rel)).filter(existsSync68);
14728
14738
  if (sourceFiles.length > 0) {
14729
14739
  const distMtime = statSync2(cliDist).mtimeMs;
14730
14740
  const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
@@ -14813,7 +14823,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
14813
14823
  }
14814
14824
  }
14815
14825
  async function readJson(file) {
14816
- if (!existsSync69(file)) return null;
14826
+ if (!existsSync68(file)) return null;
14817
14827
  try {
14818
14828
  return JSON.parse(await readFile23(file, "utf8"));
14819
14829
  } catch {
@@ -14884,11 +14894,11 @@ function extractAbsoluteHaiveBins(text) {
14884
14894
  }
14885
14895
 
14886
14896
  // src/commands/playback.ts
14887
- import { existsSync as existsSync70 } from "fs";
14897
+ import { existsSync as existsSync69 } from "fs";
14888
14898
  import "commander";
14889
14899
  import {
14890
14900
  findProjectRoot as findProjectRoot46,
14891
- loadMemoriesFromDir as loadMemoriesFromDir37,
14901
+ loadMemoriesFromDir as loadMemoriesFromDir36,
14892
14902
  parseSince as parseSince3,
14893
14903
  readUsageEvents as readUsageEvents5,
14894
14904
  resolveHaivePaths as resolveHaivePaths42
@@ -14914,7 +14924,7 @@ function registerPlayback(program2) {
14914
14924
  const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
14915
14925
  const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
14916
14926
  const sessions = bucketSessions(filtered, gapMs);
14917
- const all = existsSync70(paths.memoriesDir) ? await loadMemoriesFromDir37(paths.memoriesDir) : [];
14927
+ const all = existsSync69(paths.memoriesDir) ? await loadMemoriesFromDir36(paths.memoriesDir) : [];
14918
14928
  const memByCreatedAt = all.filter(({ memory: memory2 }) => memory2.frontmatter.type !== "session_recap").map(({ memory: memory2 }) => ({ id: memory2.frontmatter.id, at: Date.parse(memory2.frontmatter.created_at) })).sort((a, b) => a.at - b.at);
14919
14929
  const enriched = sessions.map((s, i) => {
14920
14930
  const startMs = Date.parse(s.start);
@@ -15156,11 +15166,11 @@ function runCommand3(cmd, args, cwd) {
15156
15166
  }
15157
15167
 
15158
15168
  // src/commands/welcome.ts
15159
- import { existsSync as existsSync71 } from "fs";
15169
+ import { existsSync as existsSync70 } from "fs";
15160
15170
  import "commander";
15161
15171
  import {
15162
15172
  findProjectRoot as findProjectRoot48,
15163
- loadMemoriesFromDir as loadMemoriesFromDir38,
15173
+ loadMemoriesFromDir as loadMemoriesFromDir37,
15164
15174
  resolveHaivePaths as resolveHaivePaths44
15165
15175
  } from "@hiveai/core";
15166
15176
  var TYPE_RANK = {
@@ -15178,12 +15188,12 @@ function registerWelcome(program2) {
15178
15188
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
15179
15189
  const root = findProjectRoot48(opts.dir);
15180
15190
  const paths = resolveHaivePaths44(root);
15181
- if (!existsSync71(paths.memoriesDir)) {
15191
+ if (!existsSync70(paths.memoriesDir)) {
15182
15192
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
15183
15193
  process.exitCode = 1;
15184
15194
  return;
15185
15195
  }
15186
- const all = await loadMemoriesFromDir38(paths.memoriesDir);
15196
+ const all = await loadMemoriesFromDir37(paths.memoriesDir);
15187
15197
  const team = all.filter(
15188
15198
  ({ memory: memory2 }) => memory2.frontmatter.scope === "team" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && memory2.frontmatter.status !== "stale" && memory2.frontmatter.type !== "session_recap"
15189
15199
  );
@@ -15269,7 +15279,7 @@ function registerResolveProject(program2) {
15269
15279
  }
15270
15280
 
15271
15281
  // src/commands/runtime-journal.ts
15272
- import { existsSync as existsSync73 } from "fs";
15282
+ import { existsSync as existsSync71 } from "fs";
15273
15283
  import path49 from "path";
15274
15284
  import "commander";
15275
15285
  import {
@@ -15295,7 +15305,7 @@ function registerRuntime(program2) {
15295
15305
  const root = path49.resolve(opts.dir ?? process.cwd());
15296
15306
  const paths = resolveHaivePaths45(findProjectRoot49(root));
15297
15307
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
15298
- if (!existsSync73(paths.haiveDir)) {
15308
+ if (!existsSync71(paths.haiveDir)) {
15299
15309
  ui.error("No .ai/ \u2014 run `haive init` first.");
15300
15310
  process.exitCode = 1;
15301
15311
  return;
@@ -15310,7 +15320,7 @@ function registerRuntime(program2) {
15310
15320
  }
15311
15321
 
15312
15322
  // src/commands/memory-timeline.ts
15313
- import { existsSync as existsSync74 } from "fs";
15323
+ import { existsSync as existsSync73 } from "fs";
15314
15324
  import path50 from "path";
15315
15325
  import "commander";
15316
15326
  import {
@@ -15329,13 +15339,13 @@ function registerMemoryTimeline(memory2) {
15329
15339
  }
15330
15340
  const root = path50.resolve(opts.dir ?? process.cwd());
15331
15341
  const paths = resolveHaivePaths46(findProjectRoot50(root));
15332
- if (!existsSync74(paths.memoriesDir)) {
15342
+ if (!existsSync73(paths.memoriesDir)) {
15333
15343
  ui.error("No memories \u2014 run `haive init`.");
15334
15344
  process.exitCode = 1;
15335
15345
  return;
15336
15346
  }
15337
15347
  const limit = Math.min(100, Math.max(1, parseInt(opts.limit, 10) || 30));
15338
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
15348
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
15339
15349
  const { entries, notice } = collectTimelineEntries2(all, {
15340
15350
  memoryId: opts.id,
15341
15351
  topic: opts.topic,
@@ -15347,7 +15357,7 @@ function registerMemoryTimeline(memory2) {
15347
15357
  }
15348
15358
 
15349
15359
  // src/commands/memory-conflict-candidates.ts
15350
- import { existsSync as existsSync75 } from "fs";
15360
+ import { existsSync as existsSync74 } from "fs";
15351
15361
  import path51 from "path";
15352
15362
  import "commander";
15353
15363
  import {
@@ -15372,7 +15382,7 @@ function registerMemoryConflictCandidates(memory2) {
15372
15382
  ).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
15373
15383
  const root = path51.resolve(opts.dir ?? process.cwd());
15374
15384
  const paths = resolveHaivePaths47(findProjectRoot51(root));
15375
- if (!existsSync75(paths.memoriesDir)) {
15385
+ if (!existsSync74(paths.memoriesDir)) {
15376
15386
  ui.error("No memories \u2014 run `haive init`.");
15377
15387
  process.exitCode = 1;
15378
15388
  return;
@@ -15382,7 +15392,7 @@ function registerMemoryConflictCandidates(memory2) {
15382
15392
  const maxPairs = Math.min(100, Math.max(1, parseInt(opts.maxPairs, 10) || 20));
15383
15393
  const maxScan = Math.min(2e3, Math.max(1, parseInt(opts.maxScan, 10) || 500));
15384
15394
  const maxTopicPairs = Math.min(100, Math.max(1, parseInt(opts.maxTopicPairs, 10) || 20));
15385
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
15395
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
15386
15396
  const lexical = findLexicalConflictPairs2(all, {
15387
15397
  sinceDays,
15388
15398
  types: parseTypes(opts.types),
@@ -15408,23 +15418,27 @@ function registerMemoryConflictCandidates(memory2) {
15408
15418
 
15409
15419
  // src/commands/enforce.ts
15410
15420
  import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
15411
- import { existsSync as existsSync76, statSync as statSync3 } from "fs";
15412
- import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir6, rm as rm3, writeFile as writeFile38 } from "fs/promises";
15421
+ import { existsSync as existsSync75, statSync as statSync3 } from "fs";
15422
+ import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir6, rm as rm3, writeFile as writeFile37 } from "fs/promises";
15413
15423
  import path53 from "path";
15414
15424
  import "commander";
15415
15425
  import {
15416
15426
  antiPatternGateParams as antiPatternGateParams2,
15427
+ BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
15417
15428
  findProjectRoot as findProjectRoot52,
15418
15429
  findUncapturedFailures,
15419
15430
  hasRecentBriefingMarker as hasRecentBriefingMarker2,
15420
15431
  isFreshIsoDate,
15432
+ isRetiredMemory as isRetiredMemory3,
15421
15433
  loadConfig as loadConfig14,
15422
- loadMemoriesFromDir as loadMemoriesFromDir39,
15434
+ loadMemoriesFromDir as loadMemoriesFromDir38,
15423
15435
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
15424
15436
  readRecentBriefingMarker,
15425
15437
  resolveBriefingBudget as resolveBriefingBudget3,
15426
15438
  resolveHaivePaths as resolveHaivePaths48,
15439
+ runSensors as runSensors2,
15427
15440
  saveConfig as saveConfig4,
15441
+ sensorTargetsFromDiff as sensorTargetsFromDiff2,
15428
15442
  SESSION_RECAP_TTL_MS,
15429
15443
  verifyAnchor as verifyAnchor4,
15430
15444
  writeBriefingMarker as writeBriefingMarker3
@@ -15484,14 +15498,14 @@ function registerEnforce(program2) {
15484
15498
  const root = findProjectRoot52(opts.dir);
15485
15499
  const paths = resolveHaivePaths48(root);
15486
15500
  const cacheDir = path53.join(paths.haiveDir, ".cache");
15487
- if (existsSync76(cacheDir)) {
15501
+ if (existsSync75(cacheDir)) {
15488
15502
  if (opts.dryRun) ui.info(`would clean ${path53.relative(root, cacheDir)} (preserving .gitignore)`);
15489
15503
  else {
15490
15504
  const removed = await cleanupCacheDir(cacheDir);
15491
15505
  ui.success(`cleaned ${path53.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15492
15506
  }
15493
15507
  }
15494
- if (existsSync76(paths.runtimeDir)) {
15508
+ if (existsSync75(paths.runtimeDir)) {
15495
15509
  if (opts.dryRun) ui.info(`would clean ${path53.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
15496
15510
  else {
15497
15511
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
@@ -15529,7 +15543,7 @@ function registerEnforce(program2) {
15529
15543
  const root = resolveRoot(opts.dir, payload);
15530
15544
  if (!root) return;
15531
15545
  const paths = resolveHaivePaths48(root);
15532
- if (!existsSync76(paths.haiveDir)) return;
15546
+ if (!existsSync75(paths.haiveDir)) return;
15533
15547
  await mkdir21(paths.runtimeDir, { recursive: true });
15534
15548
  const sessionId = opts.sessionId ?? payload.session_id;
15535
15549
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
@@ -15592,7 +15606,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
15592
15606
  const root = resolveRoot(opts.dir, payload);
15593
15607
  if (!root) return;
15594
15608
  const paths = resolveHaivePaths48(root);
15595
- if (!existsSync76(paths.haiveDir)) return;
15609
+ if (!existsSync75(paths.haiveDir)) return;
15596
15610
  if (!isWriteLikeTool(payload)) return;
15597
15611
  const config = await loadConfig14(paths);
15598
15612
  if (config.enforcement?.requireBriefingFirst === false) return;
@@ -15657,7 +15671,7 @@ function emitPreToolUseContext(text) {
15657
15671
  async function buildFinishReport(dir) {
15658
15672
  const root = findProjectRoot52(dir);
15659
15673
  const paths = resolveHaivePaths48(root);
15660
- const initialized = existsSync76(paths.haiveDir);
15674
+ const initialized = existsSync75(paths.haiveDir);
15661
15675
  const config = initialized ? await loadConfig14(paths) : {};
15662
15676
  const mode = config.enforcement?.mode ?? "strict";
15663
15677
  const findings = [];
@@ -15849,7 +15863,7 @@ async function checkFailureCapture(paths, config) {
15849
15863
  const gate = config.enforcement?.failureCaptureGate ?? "warn";
15850
15864
  if (gate === "off") return [];
15851
15865
  const obsFile = path53.join(paths.haiveDir, ".cache", "observations.jsonl");
15852
- if (!existsSync76(obsFile)) return [];
15866
+ if (!existsSync75(obsFile)) return [];
15853
15867
  const failures = [];
15854
15868
  try {
15855
15869
  const raw = await readFile24(obsFile, "utf8");
@@ -15866,7 +15880,7 @@ async function checkFailureCapture(paths, config) {
15866
15880
  return [];
15867
15881
  }
15868
15882
  if (failures.length === 0) return [];
15869
- const memories = existsSync76(paths.memoriesDir) ? await loadMemoriesFromDir39(paths.memoriesDir) : [];
15883
+ const memories = existsSync75(paths.memoriesDir) ? await loadMemoriesFromDir38(paths.memoriesDir) : [];
15870
15884
  const captureTimes = memories.filter(({ memory: memory2 }) => ["attempt", "gotcha"].includes(memory2.frontmatter.type)).map(({ memory: memory2 }) => memory2.frontmatter.created_at);
15871
15885
  const uncaptured = findUncapturedFailures(failures, captureTimes);
15872
15886
  if (uncaptured.length === 0) {
@@ -15901,7 +15915,7 @@ function finishReport(root, initialized, mode, findings, config) {
15901
15915
  async function runWithEnforcement(command, args, opts) {
15902
15916
  const root = findProjectRoot52(opts.dir);
15903
15917
  const paths = resolveHaivePaths48(root);
15904
- if (!existsSync76(paths.haiveDir)) {
15918
+ if (!existsSync75(paths.haiveDir)) {
15905
15919
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
15906
15920
  process.exit(1);
15907
15921
  }
@@ -15990,13 +16004,13 @@ async function writeWrapperBriefing(paths, sessionId, task) {
15990
16004
  if (briefing.setup_warnings.length > 0) {
15991
16005
  parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
15992
16006
  }
15993
- await writeFile38(file, parts.join("\n") + "\n", "utf8");
16007
+ await writeFile37(file, parts.join("\n") + "\n", "utf8");
15994
16008
  return file;
15995
16009
  }
15996
16010
  async function buildEnforcementReport(dir, stage, sessionId) {
15997
16011
  const root = findProjectRoot52(dir);
15998
16012
  const paths = resolveHaivePaths48(root);
15999
- const initialized = existsSync76(paths.haiveDir);
16013
+ const initialized = existsSync75(paths.haiveDir);
16000
16014
  const config = initialized ? await loadConfig14(paths) : {};
16001
16015
  if (initialized) {
16002
16016
  await applyLightweightRepairs(root, paths);
@@ -16030,7 +16044,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
16030
16044
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
16031
16045
  });
16032
16046
  }
16033
- findings.push(...await inspectIntegrationVersions(root, "0.18.0"));
16047
+ findings.push(...await inspectIntegrationVersions(root, "0.20.0"));
16034
16048
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
16035
16049
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
16036
16050
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -16100,8 +16114,8 @@ function withCategories(report) {
16100
16114
  };
16101
16115
  }
16102
16116
  async function hasRecentSessionRecap(paths) {
16103
- if (!existsSync76(paths.memoriesDir)) return false;
16104
- const all = await loadMemoriesFromDir39(paths.memoriesDir);
16117
+ if (!existsSync75(paths.memoriesDir)) return false;
16118
+ const all = await loadMemoriesFromDir38(paths.memoriesDir);
16105
16119
  return all.some(({ memory: memory2 }) => {
16106
16120
  const fm = memory2.frontmatter;
16107
16121
  const freshnessDate = fm.verified_at ?? fm.created_at;
@@ -16109,8 +16123,8 @@ async function hasRecentSessionRecap(paths) {
16109
16123
  });
16110
16124
  }
16111
16125
  async function verifyMemoryPolicy(paths, config) {
16112
- if (!existsSync76(paths.memoriesDir)) return [];
16113
- const all = await loadMemoriesFromDir39(paths.memoriesDir);
16126
+ if (!existsSync75(paths.memoriesDir)) return [];
16127
+ const all = await loadMemoriesFromDir38(paths.memoriesDir);
16114
16128
  const findings = [];
16115
16129
  const staleImportant = [];
16116
16130
  let verified = 0;
@@ -16147,12 +16161,12 @@ async function verifyMemoryPolicy(paths, config) {
16147
16161
  return findings;
16148
16162
  }
16149
16163
  async function verifyDecisionCoverage(paths, stage, sessionId) {
16150
- if (!existsSync76(paths.memoriesDir)) return [];
16164
+ if (!existsSync75(paths.memoriesDir)) return [];
16151
16165
  const changedFiles = (await getChangedFiles(paths.root, stage)).filter((f) => !isGeneratedArtifact(f));
16152
16166
  if (changedFiles.length === 0) {
16153
16167
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
16154
16168
  }
16155
- const all = await loadMemoriesFromDir39(paths.memoriesDir);
16169
+ const all = await loadMemoriesFromDir38(paths.memoriesDir);
16156
16170
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention"]);
16157
16171
  const relevant = all.map(({ memory: memory2 }) => memory2).filter((memory2) => {
16158
16172
  const fm = memory2.frontmatter;
@@ -16216,20 +16230,83 @@ async function runPrecommitPolicy(paths, gate, stage) {
16216
16230
  anchored_blocks,
16217
16231
  semantic: true
16218
16232
  }, { paths });
16233
+ const sensorFindings = await runSensorGate(paths, snapshot.diff);
16219
16234
  if (!result.should_block) {
16220
- return [{
16221
- severity: "ok",
16222
- code: "precommit-policy-pass",
16223
- message: `${stage === "ci" ? "CI" : "Pre-commit"} policy passed for ${touchedPaths.length} changed file(s).`
16224
- }];
16235
+ return [
16236
+ {
16237
+ severity: "ok",
16238
+ code: "precommit-policy-pass",
16239
+ message: `${stage === "ci" ? "CI" : "Pre-commit"} policy passed for ${touchedPaths.length} changed file(s).`
16240
+ },
16241
+ ...sensorFindings
16242
+ ];
16243
+ }
16244
+ return [
16245
+ {
16246
+ severity: "error",
16247
+ code: "precommit-policy-block",
16248
+ message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
16249
+ fix: "Review the hAIve warnings, then update the code or the relevant memories.",
16250
+ impact: 45
16251
+ },
16252
+ ...sensorFindings
16253
+ ];
16254
+ }
16255
+ var SENSOR_OWNED_FILES = /* @__PURE__ */ new Set([
16256
+ ...Object.values(BRIDGE_TARGET_PATH2),
16257
+ "CLAUDE.md",
16258
+ ".cursorrules",
16259
+ ".gitignore",
16260
+ ".mcp.json",
16261
+ ".cursor/mcp.json",
16262
+ ".vscode/mcp.json",
16263
+ ".cursor/rules/haive-mcp-required.mdc"
16264
+ ]);
16265
+ function isSensorScannablePath(p) {
16266
+ if (!p) return false;
16267
+ if (p.startsWith(".ai/")) return false;
16268
+ return !SENSOR_OWNED_FILES.has(p);
16269
+ }
16270
+ async function runSensorGate(paths, diff) {
16271
+ if (!diff || !existsSync75(paths.memoriesDir)) return [];
16272
+ try {
16273
+ const loaded = await loadMemoriesFromDir38(paths.memoriesDir);
16274
+ const sensorMemories = loaded.map((l) => l.memory).filter((m) => {
16275
+ const s = m.frontmatter.sensor;
16276
+ return Boolean(s) && s.kind === "regex" && !isRetiredMemory3(m.frontmatter, m.body);
16277
+ });
16278
+ if (sensorMemories.length === 0) return [];
16279
+ const targets = sensorTargetsFromDiff2(diff).filter((t) => isSensorScannablePath(t.path));
16280
+ if (targets.length === 0) return [];
16281
+ const hits = runSensors2(sensorMemories, targets);
16282
+ const findings = [];
16283
+ const seen = /* @__PURE__ */ new Set();
16284
+ for (const hit of hits) {
16285
+ if (seen.has(hit.memory_id)) continue;
16286
+ seen.add(hit.memory_id);
16287
+ const where = hit.file ? ` (${hit.file})` : "";
16288
+ if (hit.severity === "block") {
16289
+ findings.push({
16290
+ severity: "error",
16291
+ code: "sensor-block",
16292
+ message: `Block sensor fired \u2014 ${hit.memory_id}: ${hit.message}${where}`,
16293
+ fix: "Remove the flagged pattern, or run `haive sensors check` to inspect the match.",
16294
+ impact: 45
16295
+ });
16296
+ } else {
16297
+ findings.push({
16298
+ severity: "warn",
16299
+ code: "sensor-warn",
16300
+ message: `Sensor flagged ${hit.memory_id}: ${hit.message}${where}`,
16301
+ fix: "Review the flagged line; `haive sensors check` shows the matched code.",
16302
+ impact: 5
16303
+ });
16304
+ }
16305
+ }
16306
+ return findings;
16307
+ } catch {
16308
+ return [];
16225
16309
  }
16226
- return [{
16227
- severity: "error",
16228
- code: "precommit-policy-block",
16229
- message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
16230
- fix: "Review the hAIve warnings, then update the code or the relevant memories.",
16231
- impact: 45
16232
- }];
16233
16310
  }
16234
16311
  async function findGeneratedArtifacts(paths) {
16235
16312
  const dirty = await runCommand4("git", ["status", "--short", "--untracked-files=all"], paths.root).catch(() => "");
@@ -16267,9 +16344,9 @@ async function cleanupRuntimeDir(runtimeDir) {
16267
16344
  await rm3(path53.join(runtimeDir, entry.name), { recursive: true, force: true });
16268
16345
  removed++;
16269
16346
  }
16270
- await writeFile38(path53.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16271
- if (!existsSync76(path53.join(runtimeDir, "README.md"))) {
16272
- await writeFile38(
16347
+ await writeFile37(path53.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16348
+ if (!existsSync75(path53.join(runtimeDir, "README.md"))) {
16349
+ await writeFile37(
16273
16350
  path53.join(runtimeDir, "README.md"),
16274
16351
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
16275
16352
  "utf8"
@@ -16286,7 +16363,7 @@ async function cleanupCacheDir(cacheDir) {
16286
16363
  await rm3(path53.join(cacheDir, entry.name), { recursive: true, force: true });
16287
16364
  removed++;
16288
16365
  }
16289
- await writeFile38(path53.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16366
+ await writeFile37(path53.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16290
16367
  return removed;
16291
16368
  }
16292
16369
  async function cleanupEnforcementDir(enforcementDir) {
@@ -16311,7 +16388,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
16311
16388
  const findings = [];
16312
16389
  for (const rel of files) {
16313
16390
  const file = path53.join(root, rel);
16314
- if (!existsSync76(file)) continue;
16391
+ if (!existsSync75(file)) continue;
16315
16392
  const text = await readFile24(file, "utf8").catch(() => "");
16316
16393
  for (const bin of extractAbsoluteHaiveBins2(text)) {
16317
16394
  const version = versionForBinary2(bin);
@@ -16421,7 +16498,7 @@ async function resolveCiDiffRange(root) {
16421
16498
  }
16422
16499
  async function resolveGithubEventRange(root) {
16423
16500
  const eventPath = process.env.GITHUB_EVENT_PATH;
16424
- if (!eventPath || !existsSync76(eventPath)) return null;
16501
+ if (!eventPath || !existsSync75(eventPath)) return null;
16425
16502
  try {
16426
16503
  const event = JSON.parse(await readFile24(eventPath, "utf8"));
16427
16504
  const prBase = cleanGitSha(event.pull_request?.base?.sha);
@@ -16744,7 +16821,7 @@ function buildScore(findings, threshold = 80) {
16744
16821
  }
16745
16822
  async function installGitEnforcement(root) {
16746
16823
  const hooksDir = path53.join(root, ".git", "hooks");
16747
- if (!existsSync76(path53.join(root, ".git"))) {
16824
+ if (!existsSync75(path53.join(root, ".git"))) {
16748
16825
  ui.warn("No .git directory found; git enforcement hooks skipped.");
16749
16826
  return;
16750
16827
  }
@@ -16774,17 +16851,17 @@ haive enforce commit-msg "$1" --dir . || exit $?
16774
16851
  ];
16775
16852
  for (const hook of hooks) {
16776
16853
  const file = path53.join(hooksDir, hook.name);
16777
- if (existsSync76(file)) {
16854
+ if (existsSync75(file)) {
16778
16855
  const current = await readFile24(file, "utf8").catch(() => "");
16779
16856
  if (current.includes(ENFORCE_HOOK_MARKER)) {
16780
- await writeFile38(file, hook.body, "utf8");
16857
+ await writeFile37(file, hook.body, "utf8");
16781
16858
  } else {
16782
- await writeFile38(file, `${current.trimEnd()}
16859
+ await writeFile37(file, `${current.trimEnd()}
16783
16860
 
16784
16861
  ${hook.body}`, "utf8");
16785
16862
  }
16786
16863
  } else {
16787
- await writeFile38(file, hook.body, "utf8");
16864
+ await writeFile37(file, hook.body, "utf8");
16788
16865
  }
16789
16866
  await chmod2(file, 493);
16790
16867
  }
@@ -16793,11 +16870,11 @@ ${hook.body}`, "utf8");
16793
16870
  async function installCiEnforcement(root) {
16794
16871
  const workflowPath = path53.join(root, ".github", "workflows", "haive-enforcement.yml");
16795
16872
  await mkdir21(path53.dirname(workflowPath), { recursive: true });
16796
- if (existsSync76(workflowPath)) {
16873
+ if (existsSync75(workflowPath)) {
16797
16874
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
16798
16875
  return;
16799
16876
  }
16800
- await writeFile38(workflowPath, `name: haive-enforcement
16877
+ await writeFile37(workflowPath, `name: haive-enforcement
16801
16878
 
16802
16879
  on:
16803
16880
  pull_request:
@@ -16932,11 +17009,11 @@ function normalizeToolPath(file, root) {
16932
17009
  return path53.relative(root, normalized).replace(/\\/g, "/");
16933
17010
  }
16934
17011
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
16935
- if (!existsSync76(paths.memoriesDir)) return [];
17012
+ if (!existsSync75(paths.memoriesDir)) return [];
16936
17013
  const marker = await readRecentBriefingMarker(paths, sessionId);
16937
17014
  const consulted = new Set(marker?.memory_ids ?? []);
16938
17015
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
16939
- const all = await loadMemoriesFromDir39(paths.memoriesDir);
17016
+ const all = await loadMemoriesFromDir38(paths.memoriesDir);
16940
17017
  return all.filter(({ memory: memory2 }) => {
16941
17018
  const fm = memory2.frontmatter;
16942
17019
  if (!policyTypes.has(fm.type)) return false;
@@ -17017,8 +17094,8 @@ function registerRun(program2) {
17017
17094
 
17018
17095
  // src/commands/sensors.ts
17019
17096
  import { execFile as execFile3 } from "child_process";
17020
- import { existsSync as existsSync77 } from "fs";
17021
- import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile39 } from "fs/promises";
17097
+ import { existsSync as existsSync76 } from "fs";
17098
+ import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
17022
17099
  import path54 from "path";
17023
17100
  import { promisify as promisify3 } from "util";
17024
17101
  import "commander";
@@ -17027,14 +17104,14 @@ import {
17027
17104
  findProjectRoot as findProjectRoot53,
17028
17105
  isRetiredMemory as isRetiredMemory4,
17029
17106
  loadConfig as loadConfig15,
17030
- loadMemoriesFromDir as loadMemoriesFromDir40,
17107
+ loadMemoriesFromDir as loadMemoriesFromDir39,
17031
17108
  loadUsageIndex as loadUsageIndex31,
17032
17109
  recordPrevention as recordPrevention2,
17033
17110
  resolveHaivePaths as resolveHaivePaths49,
17034
- runSensors as runSensors2,
17111
+ runSensors as runSensors3,
17035
17112
  saveUsageIndex as saveUsageIndex8,
17036
17113
  selectCommandSensors,
17037
- sensorTargetsFromDiff as sensorTargetsFromDiff2,
17114
+ sensorTargetsFromDiff as sensorTargetsFromDiff3,
17038
17115
  serializeMemory as serializeMemory29
17039
17116
  } from "@hiveai/core";
17040
17117
  var exec2 = promisify3(execFile3);
@@ -17068,8 +17145,8 @@ function registerSensors(program2) {
17068
17145
  const paths = resolveHaivePaths49(root);
17069
17146
  const memories = await runnableSensorMemories(paths);
17070
17147
  const diff = opts.diffFile ? await readFile25(path54.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
17071
- const targets = sensorTargetsFromDiff2(diff);
17072
- const hits = runSensors2(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
17148
+ const targets = sensorTargetsFromDiff3(diff);
17149
+ const hits = runSensors3(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
17073
17150
  const config = await loadConfig15(paths);
17074
17151
  const runCommands = opts.commands || config.enforcement?.runCommandSensors === true;
17075
17152
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
@@ -17157,7 +17234,7 @@ function registerSensors(program2) {
17157
17234
  }
17158
17235
  const root = findProjectRoot53(opts.dir);
17159
17236
  const paths = resolveHaivePaths49(root);
17160
- const loaded = existsSync77(paths.memoriesDir) ? await loadMemoriesFromDir40(paths.memoriesDir) : [];
17237
+ const loaded = existsSync76(paths.memoriesDir) ? await loadMemoriesFromDir39(paths.memoriesDir) : [];
17161
17238
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
17162
17239
  if (!found) {
17163
17240
  ui.error(`No memory found with id ${id}`);
@@ -17177,7 +17254,7 @@ function registerSensors(program2) {
17177
17254
  },
17178
17255
  body: found.memory.body
17179
17256
  };
17180
- await writeFile39(found.filePath, serializeMemory29(next), "utf8");
17257
+ await writeFile38(found.filePath, serializeMemory29(next), "utf8");
17181
17258
  ui.success(`Updated ${id}: sensor severity=${severity}`);
17182
17259
  if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
17183
17260
  ui.info(`message=${sensor.message}`);
@@ -17196,7 +17273,7 @@ function registerSensors(program2) {
17196
17273
  await mkdir23(outDir, { recursive: true });
17197
17274
  const outPath = path54.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
17198
17275
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
17199
- await writeFile39(outPath, content, "utf8");
17276
+ await writeFile38(outPath, content, "utf8");
17200
17277
  if (format === "grep") await chmod3(outPath, 493);
17201
17278
  ui.success(`Exported ${rows.length} sensor(s): ${path54.relative(root, outPath)}`);
17202
17279
  });
@@ -17219,8 +17296,8 @@ async function sensorRows(paths) {
17219
17296
  });
17220
17297
  }
17221
17298
  async function runnableSensorMemories(paths, regexOnly = true) {
17222
- if (!existsSync77(paths.memoriesDir)) return [];
17223
- const loaded = await loadMemoriesFromDir40(paths.memoriesDir);
17299
+ if (!existsSync76(paths.memoriesDir)) return [];
17300
+ const loaded = await loadMemoriesFromDir39(paths.memoriesDir);
17224
17301
  return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
17225
17302
  const sensor = memory2.frontmatter.sensor;
17226
17303
  if (!sensor) return false;
@@ -17269,15 +17346,15 @@ function shellQuote(value) {
17269
17346
  }
17270
17347
 
17271
17348
  // src/commands/ingest.ts
17272
- import { existsSync as existsSync78 } from "fs";
17273
- import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile40 } from "fs/promises";
17349
+ import { existsSync as existsSync77 } from "fs";
17350
+ import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
17274
17351
  import path55 from "path";
17275
17352
  import "commander";
17276
17353
  import {
17277
17354
  draftsFromFindings as draftsFromFindings2,
17278
17355
  filterNewDrafts as filterNewDrafts2,
17279
17356
  findProjectRoot as findProjectRoot54,
17280
- loadMemoriesFromDir as loadMemoriesFromDir41,
17357
+ loadMemoriesFromDir as loadMemoriesFromDir40,
17281
17358
  memoryFilePath as memoryFilePath12,
17282
17359
  parseFindings as parseFindings2,
17283
17360
  resolveHaivePaths as resolveHaivePaths50,
@@ -17307,7 +17384,7 @@ function registerIngest(program2) {
17307
17384
  }
17308
17385
  const root = findProjectRoot54(opts.dir);
17309
17386
  const paths = resolveHaivePaths50(root);
17310
- if (!existsSync78(paths.haiveDir)) {
17387
+ if (!existsSync77(paths.haiveDir)) {
17311
17388
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
17312
17389
  process.exitCode = 1;
17313
17390
  return;
@@ -17329,7 +17406,7 @@ function registerIngest(program2) {
17329
17406
  return;
17330
17407
  }
17331
17408
  const reportPath = path55.resolve(root, file);
17332
- if (!existsSync78(reportPath)) {
17409
+ if (!existsSync77(reportPath)) {
17333
17410
  ui.error(`Report file not found: ${reportPath}`);
17334
17411
  process.exitCode = 1;
17335
17412
  return;
@@ -17358,7 +17435,7 @@ function registerIngest(program2) {
17358
17435
  process.exitCode = 1;
17359
17436
  return;
17360
17437
  }
17361
- const existing = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
17438
+ const existing = existsSync77(paths.memoriesDir) ? await loadMemoriesFromDir40(paths.memoriesDir) : [];
17362
17439
  const existingTopics = new Set(
17363
17440
  existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
17364
17441
  );
@@ -17424,7 +17501,7 @@ function registerIngest(program2) {
17424
17501
  async function writeDraft2(paths, draft) {
17425
17502
  const file = memoryFilePath12(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
17426
17503
  await mkdir24(path55.dirname(file), { recursive: true });
17427
- await writeFile40(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
17504
+ await writeFile39(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
17428
17505
  return file;
17429
17506
  }
17430
17507
  async function fetchSonarIssues(opts) {
@@ -17465,13 +17542,13 @@ async function fetchSonarIssues(opts) {
17465
17542
  }
17466
17543
 
17467
17544
  // src/commands/dashboard.ts
17468
- import { existsSync as existsSync79 } from "fs";
17545
+ import { existsSync as existsSync78 } from "fs";
17469
17546
  import "commander";
17470
17547
  import {
17471
17548
  buildDashboard,
17472
17549
  findProjectRoot as findProjectRoot55,
17473
17550
  loadConfig as loadConfig16,
17474
- loadMemoriesFromDir as loadMemoriesFromDir43,
17551
+ loadMemoriesFromDir as loadMemoriesFromDir41,
17475
17552
  loadPreventionEvents as loadPreventionEvents4,
17476
17553
  loadUsageIndex as loadUsageIndex33,
17477
17554
  resolveHaivePaths as resolveHaivePaths51
@@ -17482,12 +17559,12 @@ function registerDashboard(program2) {
17482
17559
  ).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) => {
17483
17560
  const root = findProjectRoot55(opts.dir);
17484
17561
  const paths = resolveHaivePaths51(root);
17485
- if (!existsSync79(paths.haiveDir)) {
17562
+ if (!existsSync78(paths.haiveDir)) {
17486
17563
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
17487
17564
  process.exitCode = 1;
17488
17565
  return;
17489
17566
  }
17490
- const memories = existsSync79(paths.memoriesDir) ? await loadMemoriesFromDir43(paths.memoriesDir) : [];
17567
+ const memories = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
17491
17568
  const usage = await loadUsageIndex33(paths);
17492
17569
  const preventionEvents = await loadPreventionEvents4(paths);
17493
17570
  const config = await loadConfig16(paths);
@@ -17601,7 +17678,7 @@ function warnNum(n) {
17601
17678
  // src/commands/dev-link.ts
17602
17679
  import { execFile as execFile4 } from "child_process";
17603
17680
  import { cp, readFile as readFile27 } from "fs/promises";
17604
- import { existsSync as existsSync80 } from "fs";
17681
+ import { existsSync as existsSync79 } from "fs";
17605
17682
  import path56 from "path";
17606
17683
  import { promisify as promisify4 } from "util";
17607
17684
  import "commander";
@@ -17611,7 +17688,7 @@ function registerDevLink(program2) {
17611
17688
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on hAIve itself.");
17612
17689
  dev.command("link").description("Hot-swap this repo's built dist into the global @hiveai install so `haive` 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) => {
17613
17690
  const root = findProjectRoot56(opts.dir);
17614
- if (!existsSync80(path56.join(root, "packages", "cli", "dist", "index.js"))) {
17691
+ if (!existsSync79(path56.join(root, "packages", "cli", "dist", "index.js"))) {
17615
17692
  ui.error(`Not the hAIve monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
17616
17693
  process.exitCode = 1;
17617
17694
  return;
@@ -17623,7 +17700,7 @@ function registerDevLink(program2) {
17623
17700
  globalModules = path56.join(path56.dirname(path56.dirname(process.execPath)), "lib", "node_modules");
17624
17701
  }
17625
17702
  const globalHive = path56.join(globalModules, "@hiveai");
17626
- if (!existsSync80(globalHive)) {
17703
+ if (!existsSync79(globalHive)) {
17627
17704
  ui.error(`No global @hiveai install at ${globalHive}. Install once with \`npm i -g @hiveai/cli\`, then re-run.`);
17628
17705
  process.exitCode = 1;
17629
17706
  return;
@@ -17631,7 +17708,7 @@ function registerDevLink(program2) {
17631
17708
  const linked = [];
17632
17709
  const copyDist = async (fromPkg, toDistDir) => {
17633
17710
  const from = path56.join(root, "packages", fromPkg, "dist");
17634
- if (!existsSync80(from) || !existsSync80(path56.dirname(toDistDir))) return;
17711
+ if (!existsSync79(from) || !existsSync79(path56.dirname(toDistDir))) return;
17635
17712
  await cp(from, toDistDir, { recursive: true });
17636
17713
  linked.push(path56.relative(globalModules, toDistDir));
17637
17714
  };
@@ -17690,7 +17767,7 @@ function registerCoverage(program2) {
17690
17767
  maxHotFiles: 500
17691
17768
  });
17692
17769
  const hotFiles = radar.hotFiles.filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes }));
17693
- const memories = await loadMemoriesFromDir28(paths.memoriesDir);
17770
+ const memories = await loadMemoriesFromDir27(paths.memoriesDir);
17694
17771
  const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
17695
17772
  if (opts.json) {
17696
17773
  console.log(JSON.stringify({ root, scanned_hot_files: hotFiles.length, gaps }, null, 2));
@@ -17718,7 +17795,7 @@ function registerCoverage(program2) {
17718
17795
 
17719
17796
  // src/commands/merge-driver.ts
17720
17797
  import { execFileSync as execFileSync3 } from "child_process";
17721
- import { readFileSync, writeFileSync, existsSync as existsSync81 } from "fs";
17798
+ import { readFileSync, writeFileSync, existsSync as existsSync80 } from "fs";
17722
17799
  import path57 from "path";
17723
17800
  import "commander";
17724
17801
  import { findProjectRoot as findProjectRoot58, mergeMemoryVersions } from "@hiveai/core";
@@ -17752,7 +17829,7 @@ function registerMergeDriver(program2) {
17752
17829
  return;
17753
17830
  }
17754
17831
  const gaPath = path57.join(root, ".gitattributes");
17755
- let content = existsSync81(gaPath) ? readFileSync(gaPath, "utf8") : "";
17832
+ let content = existsSync80(gaPath) ? readFileSync(gaPath, "utf8") : "";
17756
17833
  if (!content.includes(GITATTRIBUTES_MARK)) {
17757
17834
  if (content.length > 0 && !content.endsWith("\n")) content += "\n";
17758
17835
  content += GITATTRIBUTES_BLOCK + "\n";
@@ -17766,8 +17843,8 @@ function registerMergeDriver(program2) {
17766
17843
  }
17767
17844
 
17768
17845
  // src/commands/memory-resolve-conflict.ts
17769
- import { writeFile as writeFile41 } from "fs/promises";
17770
- import { existsSync as existsSync83 } from "fs";
17846
+ import { writeFile as writeFile40 } from "fs/promises";
17847
+ import { existsSync as existsSync81 } from "fs";
17771
17848
  import "commander";
17772
17849
  import {
17773
17850
  findProjectRoot as findProjectRoot59,
@@ -17779,12 +17856,12 @@ function registerMemoryResolveConflict(memory2) {
17779
17856
  memory2.command("resolve-conflict <id_a> <id_b>").description("Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other").option("--yes", "apply the resolution (without this, only previews it)", false).option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (idA, idB, opts) => {
17780
17857
  const root = findProjectRoot59(opts.dir);
17781
17858
  const paths = resolveHaivePaths53(root);
17782
- if (!existsSync83(paths.memoriesDir)) {
17859
+ if (!existsSync81(paths.memoriesDir)) {
17783
17860
  ui.error(`No .ai/memories at ${root}.`);
17784
17861
  process.exitCode = 1;
17785
17862
  return;
17786
17863
  }
17787
- const memories = await loadMemoriesFromDir28(paths.memoriesDir);
17864
+ const memories = await loadMemoriesFromDir27(paths.memoriesDir);
17788
17865
  const a = memories.find((m) => m.memory.frontmatter.id === idA);
17789
17866
  const b = memories.find((m) => m.memory.frontmatter.id === idB);
17790
17867
  if (!a || !b) {
@@ -17806,7 +17883,7 @@ function registerMemoryResolveConflict(memory2) {
17806
17883
  if (!opts.json) ui.info("Preview only \u2014 re-run with --yes to apply.");
17807
17884
  return;
17808
17885
  }
17809
- await writeFile41(
17886
+ await writeFile40(
17810
17887
  loser.filePath,
17811
17888
  serializeMemory31({
17812
17889
  frontmatter: {
@@ -17825,8 +17902,8 @@ function registerMemoryResolveConflict(memory2) {
17825
17902
 
17826
17903
  // src/commands/memory-seed-git.ts
17827
17904
  import { execFile as execFile5 } from "child_process";
17828
- import { mkdir as mkdir25, writeFile as writeFile43 } from "fs/promises";
17829
- import { existsSync as existsSync84 } from "fs";
17905
+ import { mkdir as mkdir25, writeFile as writeFile41 } from "fs/promises";
17906
+ import { existsSync as existsSync83 } from "fs";
17830
17907
  import path58 from "path";
17831
17908
  import { promisify as promisify5 } from "util";
17832
17909
  import "commander";
@@ -17843,7 +17920,7 @@ function registerMemorySeedGit(memory2) {
17843
17920
  memory2.command("seed-git").description("Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)").option("--apply", "write the proposed seeds as draft memories (default: preview only)", false).option("--limit <n>", "max seeds to propose", "20").option("--days <n>", "git-history lookback window in days", "365").option("--scope <scope>", "personal | team", "team").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
17844
17921
  const root = findProjectRoot60(opts.dir);
17845
17922
  const paths = resolveHaivePaths54(root);
17846
- if (!existsSync84(paths.haiveDir)) {
17923
+ if (!existsSync83(paths.haiveDir)) {
17847
17924
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
17848
17925
  process.exitCode = 1;
17849
17926
  return;
@@ -17888,9 +17965,9 @@ function registerMemorySeedGit(memory2) {
17888
17965
  _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
17889
17966
  `;
17890
17967
  const file = memoryFilePath13(paths, fm.scope, fm.id, fm.module);
17891
- if (existsSync84(file)) continue;
17968
+ if (existsSync83(file)) continue;
17892
17969
  await mkdir25(path58.dirname(file), { recursive: true });
17893
- await writeFile43(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
17970
+ await writeFile41(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
17894
17971
  written += 1;
17895
17972
  }
17896
17973
  if (!opts.json) {
@@ -17921,21 +17998,21 @@ async function readCommits(root, days) {
17921
17998
  }
17922
17999
 
17923
18000
  // src/commands/bridges.ts
17924
- import { existsSync as existsSync85 } from "fs";
18001
+ import { existsSync as existsSync84 } from "fs";
17925
18002
  import path59 from "path";
17926
18003
  import "commander";
17927
18004
  import {
17928
18005
  findProjectRoot as findProjectRoot61,
17929
18006
  resolveHaivePaths as resolveHaivePaths55,
17930
- BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
17931
- BRIDGE_TARGETS as BRIDGE_TARGETS2
18007
+ BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
18008
+ BRIDGE_TARGETS as BRIDGE_TARGETS3
17932
18009
  } from "@hiveai/core";
17933
18010
  function registerBridges(program2) {
17934
18011
  const bridges = program2.command("bridges").description(
17935
18012
  "Generate native agent bridge files from the hAIve 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 haive bridges sync --all\n haive bridges sync --only cline,windsurf\n"
17936
18013
  );
17937
18014
  bridges.command("sync").description(
17938
- "Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS2.join(", ") + "\n"
18015
+ "Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS3.join(", ") + "\n"
17939
18016
  ).option("--all", "generate all supported bridge targets").option(
17940
18017
  "--only <targets>",
17941
18018
  "comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
@@ -17943,7 +18020,7 @@ function registerBridges(program2) {
17943
18020
  const root = findProjectRoot61(opts.dir);
17944
18021
  const paths = resolveHaivePaths55(root);
17945
18022
  const dryRun = opts.dryRun === true;
17946
- if (!existsSync85(paths.memoriesDir)) {
18023
+ if (!existsSync84(paths.memoriesDir)) {
17947
18024
  ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
17948
18025
  process.exitCode = 1;
17949
18026
  return;
@@ -17951,18 +18028,18 @@ function registerBridges(program2) {
17951
18028
  let targets;
17952
18029
  if (opts.only) {
17953
18030
  const requested = opts.only.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
17954
- const invalid = requested.filter((t) => !BRIDGE_TARGETS2.includes(t));
18031
+ const invalid = requested.filter((t) => !BRIDGE_TARGETS3.includes(t));
17955
18032
  if (invalid.length > 0) {
17956
- ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS2.join(", ")}`);
18033
+ ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS3.join(", ")}`);
17957
18034
  process.exitCode = 1;
17958
18035
  return;
17959
18036
  }
17960
18037
  targets = requested;
17961
18038
  } else if (opts.all) {
17962
- targets = BRIDGE_TARGETS2;
18039
+ targets = BRIDGE_TARGETS3;
17963
18040
  } else {
17964
- targets = BRIDGE_TARGETS2.filter(
17965
- (t) => existsSync85(path59.join(root, BRIDGE_TARGET_PATH2[t]))
18041
+ targets = BRIDGE_TARGETS3.filter(
18042
+ (t) => existsSync84(path59.join(root, BRIDGE_TARGET_PATH3[t]))
17966
18043
  );
17967
18044
  if (targets.length === 0) {
17968
18045
  ui.info(
@@ -17989,9 +18066,9 @@ function registerBridges(program2) {
17989
18066
  bridges.command("list").description("List bridge targets and their status in this project").option("-d, --dir <dir>", "project root").action(async (opts) => {
17990
18067
  const root = findProjectRoot61(opts.dir);
17991
18068
  console.log(ui.bold("hAIve bridge targets:"));
17992
- for (const target of BRIDGE_TARGETS2) {
17993
- const relPath = BRIDGE_TARGET_PATH2[target];
17994
- const exists = existsSync85(path59.join(root, relPath));
18069
+ for (const target of BRIDGE_TARGETS3) {
18070
+ const relPath = BRIDGE_TARGET_PATH3[target];
18071
+ const exists = existsSync84(path59.join(root, relPath));
17995
18072
  const marker = exists ? ui.dim("\u2713") : ui.dim("\xB7");
17996
18073
  console.log(` ${marker} ${target.padEnd(10)} ${relPath}${exists ? "" : " (not present)"}`);
17997
18074
  }
@@ -18002,7 +18079,7 @@ function registerBridges(program2) {
18002
18079
 
18003
18080
  // src/index.ts
18004
18081
  var program = new Command64();
18005
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.18.0").option("--advanced", "show maintenance and experimental commands in help");
18082
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.20.0").option("--advanced", "show maintenance and experimental commands in help");
18006
18083
  registerInit(program);
18007
18084
  registerWelcome(program);
18008
18085
  registerResolveProject(program);