@hiveai/cli 0.23.0 → 0.25.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
@@ -6,7 +6,7 @@ import { Command as Command64 } from "commander";
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync as existsSync3 } from "fs";
8
8
  import { mkdir, readFile as readFile2 } from "fs/promises";
9
- import path3 from "path";
9
+ import path4 from "path";
10
10
  import "commander";
11
11
  import {
12
12
  classifyMemoryPriority,
@@ -67,6 +67,7 @@ var ui = {
67
67
 
68
68
  // src/utils/briefing-radar.ts
69
69
  import { execFile } from "child_process";
70
+ import path from "path";
70
71
  import { promisify } from "util";
71
72
  var exec = promisify(execFile);
72
73
  var DEFAULT_DAYS_BACK = 14;
@@ -94,8 +95,11 @@ var SOURCE_GLOBS = [
94
95
  ];
95
96
  async function isGitRepo(root) {
96
97
  try {
97
- await exec("git", ["rev-parse", "--is-inside-work-tree"], { cwd: root });
98
- return true;
98
+ const { stdout } = await exec("git", ["rev-parse", "--show-toplevel"], { cwd: root });
99
+ const toplevel = path.resolve(stdout.trim());
100
+ const resolvedRoot = path.resolve(root);
101
+ if (toplevel === resolvedRoot) return true;
102
+ return toplevel.startsWith(resolvedRoot + path.sep);
99
103
  } catch {
100
104
  return false;
101
105
  }
@@ -208,7 +212,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
208
212
  if (!f) continue;
209
213
  counts.set(f, (counts.get(f) ?? 0) + 1);
210
214
  }
211
- let entries = [...counts.entries()].map(([path61, changes]) => ({ path: path61, changes }));
215
+ let entries = [...counts.entries()].map(([path63, changes]) => ({ path: path63, changes }));
212
216
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
213
217
  if (lowerPaths.length > 0) {
214
218
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -238,7 +242,7 @@ function radarHasContent(r) {
238
242
  // src/utils/autopilot.ts
239
243
  import { existsSync as existsSync2 } from "fs";
240
244
  import { readFile, writeFile as writeFile2 } from "fs/promises";
241
- import path2 from "path";
245
+ import path3 from "path";
242
246
  import {
243
247
  AUTOPILOT_DEFAULTS,
244
248
  buildCodeMap,
@@ -253,7 +257,7 @@ import {
253
257
  import { existsSync } from "fs";
254
258
  import { writeFile } from "fs/promises";
255
259
  import { spawnSync } from "child_process";
256
- import path from "path";
260
+ import path2 from "path";
257
261
  import "commander";
258
262
  import {
259
263
  findProjectRoot,
@@ -441,7 +445,7 @@ function suggestAnchors(root, loaded, codeMap, trackedFiles) {
441
445
  for (const match of body.matchAll(/`([^`\n]+\.[A-Za-z0-9]+)`|(?:^|\s)([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)/gm)) {
442
446
  const candidate = (match[1] ?? match[2] ?? "").replace(/^\.?\//, "");
443
447
  if (!candidate || candidate.startsWith("http")) continue;
444
- if (existsSync(path.join(root, candidate)) && isSafeAnchorPath(candidate, trackedFiles)) {
448
+ if (existsSync(path2.join(root, candidate)) && isSafeAnchorPath(candidate, trackedFiles)) {
445
449
  paths.add(candidate);
446
450
  }
447
451
  }
@@ -704,7 +708,7 @@ async function projectContextVersionStatus(root, paths) {
704
708
  if (!existsSync2(paths.projectContext)) {
705
709
  return { mismatch: false, canSync: false };
706
710
  }
707
- const packagePath = path2.join(root, "package.json");
711
+ const packagePath = path3.join(root, "package.json");
708
712
  if (!existsSync2(packagePath)) {
709
713
  return { mismatch: false, canSync: false };
710
714
  }
@@ -940,7 +944,7 @@ function registerBriefing(program2) {
940
944
  continue;
941
945
  }
942
946
  const otherMemories = await loadMemoriesFromDir3(otherPaths.memoriesDir);
943
- const tag = path3.basename(otherRoot);
947
+ const tag = path4.basename(otherRoot);
944
948
  for (const m of otherMemories) {
945
949
  ownMemories.push({ ...m, origin: tag });
946
950
  }
@@ -1066,7 +1070,7 @@ function registerBriefing(program2) {
1066
1070
  status: item.memory.frontmatter.status,
1067
1071
  priority: priorities[i],
1068
1072
  score: item.score,
1069
- file: path3.relative(root, item.filePath),
1073
+ file: path4.relative(root, item.filePath),
1070
1074
  summary: (item.memory.body.split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l.length > 0) ?? "").slice(0, 140)
1071
1075
  }))
1072
1076
  }, null, 2));
@@ -1088,7 +1092,7 @@ function registerBriefing(program2) {
1088
1092
  `${ui.bold(fm.id)} ${priorityBadge(priority)} ${ui.dim(fm.scope + "/" + fm.type)} ${badge}${draftMarker}${unverifiedMarker}${originMarker}${hitMarker}`
1089
1093
  );
1090
1094
  if (opts.explainSource) {
1091
- const relPath = path3.relative(root, item.filePath);
1095
+ const relPath = path4.relative(root, item.filePath);
1092
1096
  const anchorPaths = fm.anchor?.paths ?? [];
1093
1097
  const anchorSymbols = fm.anchor?.symbols ?? [];
1094
1098
  const parts = [`source: ${relPath}`];
@@ -1212,7 +1216,7 @@ function registerTui(program2) {
1212
1216
 
1213
1217
  // src/commands/embeddings.ts
1214
1218
  import { existsSync as existsSync4 } from "fs";
1215
- import path4 from "path";
1219
+ import path5 from "path";
1216
1220
  import "commander";
1217
1221
  import { findProjectRoot as findProjectRoot4, resolveHaivePaths as resolveHaivePaths3 } from "@hiveai/core";
1218
1222
  function registerEmbeddings(program2) {
@@ -1254,7 +1258,7 @@ function registerEmbeddings(program2) {
1254
1258
  for (const hit of result.hits) {
1255
1259
  const score = hit.score.toFixed(3);
1256
1260
  console.log(`${ui.bold(score)} ${hit.id}`);
1257
- console.log(` ${ui.dim(path4.relative(root, hit.file_path))}`);
1261
+ console.log(` ${ui.dim(path5.relative(root, hit.file_path))}`);
1258
1262
  }
1259
1263
  });
1260
1264
  embeddings.command("status").description("Show the embeddings index status").option("-d, --dir <dir>", "project root").action(async (opts) => {
@@ -1285,7 +1289,7 @@ async function loadEmbeddings() {
1285
1289
 
1286
1290
  // src/commands/index-code.ts
1287
1291
  import { existsSync as existsSync5, statSync } from "fs";
1288
- import path5 from "path";
1292
+ import path6 from "path";
1289
1293
  import "commander";
1290
1294
  import {
1291
1295
  buildCodeMap as buildCodeMap2,
@@ -1334,7 +1338,7 @@ function registerIndexCode(program2) {
1334
1338
  const fileCount = Object.keys(map.files).length;
1335
1339
  const exportCount = Object.values(map.files).reduce((s, f) => s + f.exports.length, 0);
1336
1340
  ui.success(
1337
- `Indexed ${fileCount} file(s) with ${exportCount} export(s) \u2192 ${path5.relative(root, codeMapPath(paths))}`
1341
+ `Indexed ${fileCount} file(s) with ${exportCount} export(s) \u2192 ${path6.relative(root, codeMapPath(paths))}`
1338
1342
  );
1339
1343
  });
1340
1344
  idx.command("code-search").description(
@@ -1371,12 +1375,12 @@ async function reportIndexStatus(root, paths, asJson) {
1371
1375
  const fileCount = map ? Object.keys(map.files).length : 0;
1372
1376
  const exportCount = map ? Object.values(map.files).reduce((s, f) => s + f.exports.length, 0) : 0;
1373
1377
  const mapMtime = existsSync5(mapFile) ? statSync(mapFile).mtime.toISOString() : null;
1374
- const searchIndexFile = path5.join(paths.haiveDir, ".cache", "embeddings", "code-embeddings-index.json");
1378
+ const searchIndexFile = path6.join(paths.haiveDir, ".cache", "embeddings", "code-embeddings-index.json");
1375
1379
  const searchIndexPresent = existsSync5(searchIndexFile);
1376
1380
  const status = {
1377
1381
  code_map: {
1378
1382
  present: map !== null,
1379
- path: path5.relative(root, mapFile),
1383
+ path: path6.relative(root, mapFile),
1380
1384
  files: fileCount,
1381
1385
  exports: exportCount,
1382
1386
  generated_at: map?.generated_at ?? null,
@@ -1384,7 +1388,7 @@ async function reportIndexStatus(root, paths, asJson) {
1384
1388
  },
1385
1389
  code_search_index: {
1386
1390
  present: searchIndexPresent,
1387
- path: path5.relative(root, searchIndexFile)
1391
+ path: path6.relative(root, searchIndexFile)
1388
1392
  }
1389
1393
  };
1390
1394
  if (asJson) {
@@ -1409,7 +1413,7 @@ async function reportIndexStatus(root, paths, asJson) {
1409
1413
  import { execFile as execFile2 } from "child_process";
1410
1414
  import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
1411
1415
  import { existsSync as existsSync11 } from "fs";
1412
- import path11 from "path";
1416
+ import path12 from "path";
1413
1417
  import { spawnSync as spawnSync3 } from "child_process";
1414
1418
  import { promisify as promisify2 } from "util";
1415
1419
  import "commander";
@@ -1430,7 +1434,7 @@ import {
1430
1434
  // src/utils/bridge-files.ts
1431
1435
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
1432
1436
  import { existsSync as existsSync6 } from "fs";
1433
- import path6 from "path";
1437
+ import path7 from "path";
1434
1438
  import {
1435
1439
  BRIDGE_MARKERS,
1436
1440
  generateBridges,
@@ -1457,14 +1461,14 @@ async function writeBridgeFiles(root, paths, opts) {
1457
1461
  const maxMemories = Math.max(1, opts.maxMemories ?? 8);
1458
1462
  const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
1459
1463
  for (const output of outputs) {
1460
- const targetFile = path6.join(root, output.path);
1464
+ const targetFile = path7.join(root, output.path);
1461
1465
  const fileExists = existsSync6(targetFile);
1462
1466
  if (opts.onlyExisting && !fileExists) continue;
1463
1467
  if (opts.dryRun) {
1464
1468
  (fileExists ? result.updated : result.created).push(output.path);
1465
1469
  continue;
1466
1470
  }
1467
- await mkdir2(path6.dirname(targetFile), { recursive: true });
1471
+ await mkdir2(path7.dirname(targetFile), { recursive: true });
1468
1472
  if (!fileExists) {
1469
1473
  await writeFile3(targetFile, output.content, "utf8");
1470
1474
  result.created.push(output.path);
@@ -1513,7 +1517,7 @@ import { spawnSync as spawnSync2 } from "child_process";
1513
1517
  import { existsSync as existsSync8 } from "fs";
1514
1518
  import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
1515
1519
  import os2 from "os";
1516
- import path8 from "path";
1520
+ import path9 from "path";
1517
1521
  import { createInterface } from "readline/promises";
1518
1522
  import "commander";
1519
1523
  import { findProjectRoot as findProjectRoot6, resolveHaivePaths as resolveHaivePaths5 } from "@hiveai/core";
@@ -1521,7 +1525,7 @@ import { findProjectRoot as findProjectRoot6, resolveHaivePaths as resolveHaiveP
1521
1525
  // src/commands/init-mcp-setup.ts
1522
1526
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
1523
1527
  import { existsSync as existsSync7 } from "fs";
1524
- import path7 from "path";
1528
+ import path8 from "path";
1525
1529
  import os from "os";
1526
1530
  var HOME = os.homedir();
1527
1531
  var HAIVE_MCP_ENTRY = {
@@ -1536,11 +1540,11 @@ function projectMcpEntry(root) {
1536
1540
  };
1537
1541
  }
1538
1542
  function cursorMcpPath() {
1539
- return path7.join(HOME, ".cursor", "mcp.json");
1543
+ return path8.join(HOME, ".cursor", "mcp.json");
1540
1544
  }
1541
1545
  async function configureCursor() {
1542
1546
  const mcpPath = cursorMcpPath();
1543
- const cursorDir = path7.join(HOME, ".cursor");
1547
+ const cursorDir = path8.join(HOME, ".cursor");
1544
1548
  if (!existsSync7(cursorDir)) return { client: "Cursor", status: "not_installed" };
1545
1549
  let config = {};
1546
1550
  if (existsSync7(mcpPath)) {
@@ -1558,16 +1562,16 @@ async function configureCursor() {
1558
1562
  }
1559
1563
  function vscodeMcpPath() {
1560
1564
  const candidates = [
1561
- path7.join(HOME, ".config", "Code", "User", "mcp.json"),
1565
+ path8.join(HOME, ".config", "Code", "User", "mcp.json"),
1562
1566
  // Linux
1563
- path7.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1567
+ path8.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1564
1568
  // macOS
1565
- path7.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1569
+ path8.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1566
1570
  // Windows
1567
- path7.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1571
+ path8.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1568
1572
  ];
1569
1573
  for (const c of candidates) {
1570
- if (existsSync7(path7.dirname(c))) return c;
1574
+ if (existsSync7(path8.dirname(c))) return c;
1571
1575
  }
1572
1576
  return null;
1573
1577
  }
@@ -1584,20 +1588,20 @@ async function configureVSCode() {
1584
1588
  config.servers ??= {};
1585
1589
  if (config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
1586
1590
  config.servers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1587
- await mkdir3(path7.dirname(mcpPath), { recursive: true });
1591
+ await mkdir3(path8.dirname(mcpPath), { recursive: true });
1588
1592
  await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1589
1593
  return { client: "VS Code", status: "configured", path: mcpPath };
1590
1594
  }
1591
1595
  function claudeConfigPath() {
1592
- const p = path7.join(HOME, ".claude.json");
1596
+ const p = path8.join(HOME, ".claude.json");
1593
1597
  if (existsSync7(p)) return p;
1594
- const p2 = path7.join(HOME, ".config", "claude", "claude.json");
1595
- if (existsSync7(path7.dirname(p2))) return p2;
1598
+ const p2 = path8.join(HOME, ".config", "claude", "claude.json");
1599
+ if (existsSync7(path8.dirname(p2))) return p2;
1596
1600
  return null;
1597
1601
  }
1598
1602
  async function configureClaude() {
1599
- const cfgPath = claudeConfigPath() ?? path7.join(HOME, ".claude.json");
1600
- if (!existsSync7(cfgPath) && !existsSync7(path7.join(HOME, ".claude"))) {
1603
+ const cfgPath = claudeConfigPath() ?? path8.join(HOME, ".claude.json");
1604
+ if (!existsSync7(cfgPath) && !existsSync7(path8.join(HOME, ".claude"))) {
1601
1605
  return { client: "Claude Code", status: "not_installed" };
1602
1606
  }
1603
1607
  let config = {};
@@ -1615,11 +1619,11 @@ async function configureClaude() {
1615
1619
  }
1616
1620
  function windsurfMcpPath() {
1617
1621
  const candidates = [
1618
- path7.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1619
- path7.join(HOME, ".windsurf", "mcp.json")
1622
+ path8.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1623
+ path8.join(HOME, ".windsurf", "mcp.json")
1620
1624
  ];
1621
1625
  for (const c of candidates) {
1622
- if (existsSync7(path7.dirname(c))) return c;
1626
+ if (existsSync7(path8.dirname(c))) return c;
1623
1627
  }
1624
1628
  return null;
1625
1629
  }
@@ -1636,7 +1640,7 @@ async function configureWindsurf() {
1636
1640
  config.mcpServers ??= {};
1637
1641
  if (config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
1638
1642
  config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
1639
- await mkdir3(path7.dirname(mcpPath), { recursive: true });
1643
+ await mkdir3(path8.dirname(mcpPath), { recursive: true });
1640
1644
  await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
1641
1645
  return { client: "Windsurf", status: "configured", path: mcpPath };
1642
1646
  }
@@ -1657,7 +1661,7 @@ async function configureProjectMcpClients(root) {
1657
1661
  const entry = projectMcpEntry(root);
1658
1662
  const results = [];
1659
1663
  try {
1660
- const cursorPath = path7.join(root, ".cursor", "mcp.json");
1664
+ const cursorPath = path8.join(root, ".cursor", "mcp.json");
1661
1665
  let config = {};
1662
1666
  if (existsSync7(cursorPath)) {
1663
1667
  try {
@@ -1667,14 +1671,14 @@ async function configureProjectMcpClients(root) {
1667
1671
  }
1668
1672
  config.mcpServers ??= {};
1669
1673
  config.mcpServers["haive"] = entry;
1670
- await mkdir3(path7.dirname(cursorPath), { recursive: true });
1674
+ await mkdir3(path8.dirname(cursorPath), { recursive: true });
1671
1675
  await writeFile4(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1672
1676
  results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
1673
1677
  } catch (err) {
1674
1678
  results.push({ client: "Cursor (project)", status: "error", error: String(err) });
1675
1679
  }
1676
1680
  try {
1677
- const vscodePath = path7.join(root, ".vscode", "mcp.json");
1681
+ const vscodePath = path8.join(root, ".vscode", "mcp.json");
1678
1682
  let config = {};
1679
1683
  if (existsSync7(vscodePath)) {
1680
1684
  try {
@@ -1684,14 +1688,14 @@ async function configureProjectMcpClients(root) {
1684
1688
  }
1685
1689
  config.servers ??= {};
1686
1690
  config.servers["haive"] = { ...entry, type: "stdio" };
1687
- await mkdir3(path7.dirname(vscodePath), { recursive: true });
1691
+ await mkdir3(path8.dirname(vscodePath), { recursive: true });
1688
1692
  await writeFile4(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
1689
1693
  results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
1690
1694
  } catch (err) {
1691
1695
  results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
1692
1696
  }
1693
1697
  try {
1694
- const mcpPath = path7.join(root, ".mcp.json");
1698
+ const mcpPath = path8.join(root, ".mcp.json");
1695
1699
  let config = {};
1696
1700
  if (existsSync7(mcpPath)) {
1697
1701
  try {
@@ -1767,9 +1771,9 @@ async function detectAgentMode(dir) {
1767
1771
  const root = findProjectRoot6(dir);
1768
1772
  const paths = resolveHaivePaths5(root);
1769
1773
  const projectMcp = [
1770
- { client: "Claude Code", path: path8.join(root, ".mcp.json"), present: existsSync8(path8.join(root, ".mcp.json")) },
1771
- { client: "Cursor", path: path8.join(root, ".cursor", "mcp.json"), present: existsSync8(path8.join(root, ".cursor", "mcp.json")) },
1772
- { client: "VS Code", path: path8.join(root, ".vscode", "mcp.json"), present: existsSync8(path8.join(root, ".vscode", "mcp.json")) }
1774
+ { client: "Claude Code", path: path9.join(root, ".mcp.json"), present: existsSync8(path9.join(root, ".mcp.json")) },
1775
+ { client: "Cursor", path: path9.join(root, ".cursor", "mcp.json"), present: existsSync8(path9.join(root, ".cursor", "mcp.json")) },
1776
+ { client: "VS Code", path: path9.join(root, ".vscode", "mcp.json"), present: existsSync8(path9.join(root, ".vscode", "mcp.json")) }
1773
1777
  ];
1774
1778
  const installedAgents = [
1775
1779
  { agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
@@ -1792,9 +1796,9 @@ async function detectAgentMode(dir) {
1792
1796
  };
1793
1797
  }
1794
1798
  async function writeAgentModeRecord(paths, detection, skippedReason) {
1795
- const dir = path8.join(paths.runtimeDir, "enforcement");
1799
+ const dir = path9.join(paths.runtimeDir, "enforcement");
1796
1800
  await mkdir4(dir, { recursive: true });
1797
- const file = path8.join(dir, "agent-mode.json");
1801
+ const file = path9.join(dir, "agent-mode.json");
1798
1802
  const record = {
1799
1803
  selected_mode: detection.recommended_mode,
1800
1804
  recommended_command: detection.recommended_command,
@@ -1835,7 +1839,7 @@ async function configureCodexIfAvailable(root) {
1835
1839
  "mcp",
1836
1840
  "--stdio"
1837
1841
  ], { encoding: "utf8" });
1838
- if (result.status === 0) return { client: "Codex", status: "configured", path: path8.join(os2.homedir(), ".codex", "config.toml") };
1842
+ if (result.status === 0) return { client: "Codex", status: "configured", path: path9.join(os2.homedir(), ".codex", "config.toml") };
1839
1843
  return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
1840
1844
  }
1841
1845
  function commandExists(command) {
@@ -1862,7 +1866,7 @@ function printDetection(detection, json) {
1862
1866
  console.log(ui.dim(` root: ${detection.root}`));
1863
1867
  console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
1864
1868
  for (const cfg of detection.project_mcp) {
1865
- console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path8.relative(detection.root, cfg.path))}`);
1869
+ console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path9.relative(detection.root, cfg.path))}`);
1866
1870
  }
1867
1871
  for (const agent of detection.installed_agents) {
1868
1872
  const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
@@ -1892,7 +1896,7 @@ function printSetupResult(result) {
1892
1896
  // src/commands/init-bootstrap.ts
1893
1897
  import { readdir, readFile as readFile5 } from "fs/promises";
1894
1898
  import { existsSync as existsSync9, readdirSync } from "fs";
1895
- import path9 from "path";
1899
+ import path10 from "path";
1896
1900
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
1897
1901
  "node_modules",
1898
1902
  "dist",
@@ -1978,12 +1982,12 @@ function detectKeyDeps(allDeps) {
1978
1982
  return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
1979
1983
  }
1980
1984
  function detectLanguage(root) {
1981
- if (existsSync9(path9.join(root, "tsconfig.json"))) return "TypeScript";
1982
- if (existsSync9(path9.join(root, "pyproject.toml")) || existsSync9(path9.join(root, "setup.py"))) return "Python";
1983
- if (existsSync9(path9.join(root, "go.mod"))) return "Go";
1984
- if (existsSync9(path9.join(root, "pom.xml")) || existsSync9(path9.join(root, "build.gradle"))) return "Java/Kotlin";
1985
- if (existsSync9(path9.join(root, "Cargo.toml"))) return "Rust";
1986
- if (existsSync9(path9.join(root, "package.json"))) {
1985
+ if (existsSync9(path10.join(root, "tsconfig.json"))) return "TypeScript";
1986
+ if (existsSync9(path10.join(root, "pyproject.toml")) || existsSync9(path10.join(root, "setup.py"))) return "Python";
1987
+ if (existsSync9(path10.join(root, "go.mod"))) return "Go";
1988
+ if (existsSync9(path10.join(root, "pom.xml")) || existsSync9(path10.join(root, "build.gradle"))) return "Java/Kotlin";
1989
+ if (existsSync9(path10.join(root, "Cargo.toml"))) return "Rust";
1990
+ if (existsSync9(path10.join(root, "package.json"))) {
1987
1991
  return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
1988
1992
  }
1989
1993
  return "Unknown";
@@ -2003,7 +2007,7 @@ function hasSourceWithExt(root, exts) {
2003
2007
  if (depth <= 0) return false;
2004
2008
  for (const entry of entries) {
2005
2009
  if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
2006
- if (scanDir(path9.join(dir, entry.name), depth - 1)) return true;
2010
+ if (scanDir(path10.join(dir, entry.name), depth - 1)) return true;
2007
2011
  }
2008
2012
  }
2009
2013
  return false;
@@ -2040,9 +2044,9 @@ async function scanDirs(root, maxDepth = 2) {
2040
2044
  for (const entry of entries) {
2041
2045
  if (!entry.isDirectory()) continue;
2042
2046
  if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
2043
- const rel = path9.relative(root, path9.join(dir, entry.name));
2047
+ const rel = path10.relative(root, path10.join(dir, entry.name));
2044
2048
  results.push(rel);
2045
- await walk(path9.join(dir, entry.name), depth + 1);
2049
+ await walk(path10.join(dir, entry.name), depth + 1);
2046
2050
  }
2047
2051
  }
2048
2052
  await walk(root, 0);
@@ -2159,7 +2163,7 @@ function readmeExcerpt(readme) {
2159
2163
  }
2160
2164
  async function generateBootstrapContext(root) {
2161
2165
  let pkg = {};
2162
- const pkgPath = path9.join(root, "package.json");
2166
+ const pkgPath = path10.join(root, "package.json");
2163
2167
  if (existsSync9(pkgPath)) {
2164
2168
  try {
2165
2169
  pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
@@ -2172,11 +2176,11 @@ async function generateBootstrapContext(root) {
2172
2176
  const language = detectLanguage(root);
2173
2177
  const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
2174
2178
  const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
2175
- const projectName = pkg.name ?? path9.basename(root);
2179
+ const projectName = pkg.name ?? path10.basename(root);
2176
2180
  const projectDesc = pkg.description ?? "";
2177
2181
  let readmeSummary = "";
2178
2182
  for (const name of ["README.md", "readme.md", "README"]) {
2179
- const p = path9.join(root, name);
2183
+ const p = path10.join(root, name);
2180
2184
  if (existsSync9(p)) {
2181
2185
  try {
2182
2186
  const content = await readFile5(p, "utf8");
@@ -2245,10 +2249,11 @@ async function generateBootstrapContext(root) {
2245
2249
  // src/commands/init-stack-packs.ts
2246
2250
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
2247
2251
  import { existsSync as existsSync10 } from "fs";
2248
- import path10 from "path";
2252
+ import path11 from "path";
2249
2253
  import {
2250
2254
  buildFrontmatter,
2251
2255
  loadMemoriesFromDir as loadMemoriesFromDir5,
2256
+ meetsSeedQualityFloor,
2252
2257
  memoryFilePath,
2253
2258
  serializeMemory as serializeMemory2,
2254
2259
  STACK_PACK_TAG
@@ -2289,7 +2294,12 @@ app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: t
2289
2294
  body: `Controllers must never import Prisma/TypeORM directly \u2014 that belongs in Services.
2290
2295
 
2291
2296
  Controller \u2192 Service \u2192 Repository (or direct ORM) is the required layering.
2292
- Direct ORM usage in controllers makes testing impossible and couples transport to persistence.`
2297
+ Direct ORM usage in controllers makes testing impossible and couples transport to persistence.`,
2298
+ sensor: {
2299
+ pattern: "@prisma/client|PrismaClient|getRepository\\(|createQueryBuilder\\(",
2300
+ paths: ["**/*.controller.ts"],
2301
+ message: "ORM/Prisma used directly in a controller \u2014 move persistence into a Service (Controller \u2192 Service \u2192 Repository)."
2302
+ }
2293
2303
  },
2294
2304
  {
2295
2305
  slug: "nestjs-exception-filter-for-prisma",
@@ -2474,7 +2484,11 @@ Routes without schema accept any body and bypass Fastify's fast-json-stringify s
2474
2484
 
2475
2485
  Calling $disconnect() after each request wastes the warm connection pool.
2476
2486
  Create one PrismaClient per process (module-level singleton), not per request.
2477
- Disconnecting is only needed when the process is shutting down.`
2487
+ Disconnecting is only needed when the process is shutting down.`,
2488
+ sensor: {
2489
+ pattern: "\\$disconnect\\(\\)",
2490
+ message: "prisma.$disconnect() per request drains the warm connection pool in serverless \u2014 use a module-level PrismaClient singleton and only disconnect on shutdown."
2491
+ }
2478
2492
  },
2479
2493
  {
2480
2494
  slug: "prisma-migrations-never-modify",
@@ -2530,7 +2544,11 @@ const store = useStore();
2530
2544
  const count = useStore((s) => s.count);
2531
2545
  \`\`\`
2532
2546
 
2533
- Subscribing to the whole store is the single most common Zustand performance mistake.`
2547
+ Subscribing to the whole store is the single most common Zustand performance mistake.`,
2548
+ sensor: {
2549
+ pattern: "use[A-Z]\\w*Store\\(\\s*\\)",
2550
+ message: "A Zustand store hook called with no selector subscribes to the WHOLE store (re-renders on any change) \u2014 pass a slice selector: useStore(s => s.field)."
2551
+ }
2534
2552
  },
2535
2553
  {
2536
2554
  slug: "zustand-devtools-wrap-dev-only",
@@ -2751,7 +2769,8 @@ const users = await User.find({});
2751
2769
  const users = await User.find({}).lean();
2752
2770
  \`\`\`
2753
2771
 
2754
- Never use .lean() when you need to call .save() or Mongoose instance methods.`
2772
+ \`.lean()\` skips hydration into a Mongoose \`Document\`, so getters, virtuals, \`toObject()\` and
2773
+ instance methods are gone. Never use \`.lean()\` when you then call \`.save()\` or instance methods.`
2755
2774
  },
2756
2775
  {
2757
2776
  slug: "mongoose-index-frequently-queried-fields",
@@ -2970,7 +2989,11 @@ A committed key lets anyone forge sessions and CSRF tokens.`
2970
2989
  db.execute(f"SELECT * FROM users WHERE id = {uid}")
2971
2990
  # \u2705
2972
2991
  db.execute("SELECT * FROM users WHERE id = %s", (uid,))
2973
- \`\`\``
2992
+ \`\`\``,
2993
+ sensor: {
2994
+ pattern: `execute\\(\\s*f["']`,
2995
+ message: 'SQL built with an f-string inside execute() \u2014 SQL injection risk; use a parameterized query: execute("\u2026 %s \u2026", (params,)).'
2996
+ }
2974
2997
  }
2975
2998
  ],
2976
2999
  vue: [
@@ -3338,6 +3361,7 @@ async function seedStackPack(haivePaths, stack) {
3338
3361
  let memCount = 0;
3339
3362
  let sensorCount = 0;
3340
3363
  for (const mem of memories) {
3364
+ if (!meetsSeedQualityFloor(mem.body, Boolean(mem.sensor))) continue;
3341
3365
  const sensor = mem.sensor ? {
3342
3366
  kind: "regex",
3343
3367
  pattern: mem.sensor.pattern,
@@ -3374,7 +3398,7 @@ ${mem.body}`;
3374
3398
  const content = serializeMemory2({ frontmatter: fm, body: `${titledBody}
3375
3399
 
3376
3400
  ${SEED_FOOTER(stack)}` });
3377
- await mkdir5(path10.dirname(filePath), { recursive: true });
3401
+ await mkdir5(path11.dirname(filePath), { recursive: true });
3378
3402
  await writeFile6(filePath, content, "utf8");
3379
3403
  existingTopics.add(topic);
3380
3404
  existingSignatures.add(signature);
@@ -3386,7 +3410,7 @@ ${SEED_FOOTER(stack)}` });
3386
3410
 
3387
3411
  // src/commands/init.ts
3388
3412
  var execFileAsync = promisify2(execFile2);
3389
- var HAIVE_GITHUB_ACTION_REF = `v${"0.23.0"}`;
3413
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.25.0"}`;
3390
3414
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3391
3415
 
3392
3416
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3614,7 +3638,7 @@ function registerInit(program2) {
3614
3638
  "approve user-level AI client configuration prompts during agent setup",
3615
3639
  false
3616
3640
  ).option("--json", "emit a machine-readable summary on stdout (human logs go to stderr)", false).action(async (opts) => {
3617
- const root = path11.resolve(opts.dir);
3641
+ const root = path12.resolve(opts.dir);
3618
3642
  const paths = resolveHaivePaths6(root);
3619
3643
  const autopilot = opts.manual !== true;
3620
3644
  const json = opts.json === true;
@@ -3641,7 +3665,7 @@ function registerInit(program2) {
3641
3665
  await mkdir6(paths.moduleDir, { recursive: true });
3642
3666
  await mkdir6(paths.modulesContextDir, { recursive: true });
3643
3667
  await ensureAiRuntimeLayout(paths.runtimeDir);
3644
- await ensureAiCacheLayout(path11.join(paths.haiveDir, ".cache"));
3668
+ await ensureAiCacheLayout(path12.join(paths.haiveDir, ".cache"));
3645
3669
  if (!existsSync11(paths.projectContext)) {
3646
3670
  if (wantBootstrap) {
3647
3671
  ui.info("Bootstrapping project context from local files\u2026");
@@ -3655,11 +3679,11 @@ function registerInit(program2) {
3655
3679
  }
3656
3680
  } else {
3657
3681
  await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3658
- ui.success(`Created ${path11.relative(root, paths.projectContext)}`);
3682
+ ui.success(`Created ${path12.relative(root, paths.projectContext)}`);
3659
3683
  }
3660
3684
  }
3661
3685
  const configExists = existsSync11(
3662
- path11.join(paths.haiveDir, "haive.config.json")
3686
+ path12.join(paths.haiveDir, "haive.config.json")
3663
3687
  );
3664
3688
  if (!configExists) {
3665
3689
  await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
@@ -3726,13 +3750,13 @@ function registerInit(program2) {
3726
3750
  }
3727
3751
  const wantCi = opts.withCi || autopilot;
3728
3752
  if (wantCi) {
3729
- const ciPath = path11.join(root, ".github", "workflows", "haive-sync.yml");
3753
+ const ciPath = path12.join(root, ".github", "workflows", "haive-sync.yml");
3730
3754
  if (existsSync11(ciPath)) {
3731
3755
  ui.info("CI workflow already exists \u2014 skipped");
3732
3756
  } else {
3733
- await mkdir6(path11.dirname(ciPath), { recursive: true });
3757
+ await mkdir6(path12.dirname(ciPath), { recursive: true });
3734
3758
  await writeFile7(ciPath, CI_WORKFLOW, "utf8");
3735
- ui.success(`Created ${path11.relative(root, ciPath)}`);
3759
+ ui.success(`Created ${path12.relative(root, ciPath)}`);
3736
3760
  }
3737
3761
  }
3738
3762
  if (autopilot) {
@@ -3772,7 +3796,7 @@ function registerInit(program2) {
3772
3796
  interactive: process.stdin.isTTY
3773
3797
  });
3774
3798
  for (const r of agentSetup.project_results) {
3775
- if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path11.relative(root, r.path)})`);
3799
+ if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path12.relative(root, r.path)})`);
3776
3800
  else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
3777
3801
  }
3778
3802
  for (const r of agentSetup.global_results) {
@@ -3871,7 +3895,7 @@ async function autoDetectStacksFromRoot(root) {
3871
3895
  let requirementsTxt;
3872
3896
  let goMod;
3873
3897
  let pomXml;
3874
- const pkgPath = path11.join(root, "package.json");
3898
+ const pkgPath = path12.join(root, "package.json");
3875
3899
  if (existsSync11(pkgPath)) {
3876
3900
  try {
3877
3901
  const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
@@ -3880,7 +3904,7 @@ async function autoDetectStacksFromRoot(root) {
3880
3904
  }
3881
3905
  }
3882
3906
  for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
3883
- const reqPath = path11.join(root, name);
3907
+ const reqPath = path12.join(root, name);
3884
3908
  if (existsSync11(reqPath)) {
3885
3909
  try {
3886
3910
  requirementsTxt = await readFile6(reqPath, "utf8");
@@ -3889,14 +3913,14 @@ async function autoDetectStacksFromRoot(root) {
3889
3913
  }
3890
3914
  }
3891
3915
  }
3892
- const goModPath = path11.join(root, "go.mod");
3916
+ const goModPath = path12.join(root, "go.mod");
3893
3917
  if (existsSync11(goModPath)) {
3894
3918
  try {
3895
3919
  goMod = await readFile6(goModPath, "utf8");
3896
3920
  } catch {
3897
3921
  }
3898
3922
  }
3899
- const pomPath = path11.join(root, "pom.xml");
3923
+ const pomPath = path12.join(root, "pom.xml");
3900
3924
  if (existsSync11(pomPath)) {
3901
3925
  try {
3902
3926
  pomXml = await readFile6(pomPath, "utf8");
@@ -3904,7 +3928,7 @@ async function autoDetectStacksFromRoot(root) {
3904
3928
  }
3905
3929
  }
3906
3930
  let composerJson;
3907
- const composerPath = path11.join(root, "composer.json");
3931
+ const composerPath = path12.join(root, "composer.json");
3908
3932
  if (existsSync11(composerPath)) {
3909
3933
  try {
3910
3934
  composerJson = await readFile6(composerPath, "utf8");
@@ -3912,16 +3936,16 @@ async function autoDetectStacksFromRoot(root) {
3912
3936
  }
3913
3937
  }
3914
3938
  let gemfile;
3915
- const gemfilePath = path11.join(root, "Gemfile");
3939
+ const gemfilePath = path12.join(root, "Gemfile");
3916
3940
  if (existsSync11(gemfilePath)) {
3917
3941
  try {
3918
3942
  gemfile = await readFile6(gemfilePath, "utf8");
3919
3943
  } catch {
3920
3944
  }
3921
3945
  }
3922
- const hasDockerfile = existsSync11(path11.join(root, "Dockerfile"));
3923
- const hasTurboJson = existsSync11(path11.join(root, "turbo.json"));
3924
- const hasNxJson = existsSync11(path11.join(root, "nx.json"));
3946
+ const hasDockerfile = existsSync11(path12.join(root, "Dockerfile"));
3947
+ const hasTurboJson = existsSync11(path12.join(root, "turbo.json"));
3948
+ const hasNxJson = existsSync11(path12.join(root, "nx.json"));
3925
3949
  let hasCsproj = false;
3926
3950
  try {
3927
3951
  const entries = await readdir2(root);
@@ -3974,7 +3998,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
3974
3998
  `;
3975
3999
  const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
3976
4000
  if (existsSync11(file)) continue;
3977
- await mkdir6(path11.dirname(file), { recursive: true });
4001
+ await mkdir6(path12.dirname(file), { recursive: true });
3978
4002
  await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
3979
4003
  written++;
3980
4004
  }
@@ -4036,12 +4060,12 @@ function printInitReport(r) {
4036
4060
  }
4037
4061
  async function writeCursorHaiveRule(root) {
4038
4062
  const relPath = ".cursor/rules/haive-mcp-required.mdc";
4039
- const target = path11.join(root, relPath);
4063
+ const target = path12.join(root, relPath);
4040
4064
  if (existsSync11(target)) {
4041
4065
  ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
4042
4066
  return;
4043
4067
  }
4044
- await mkdir6(path11.dirname(target), { recursive: true });
4068
+ await mkdir6(path12.dirname(target), { recursive: true });
4045
4069
  await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
4046
4070
  ui.success(`Created Cursor rule ${relPath}`);
4047
4071
  }
@@ -4070,25 +4094,25 @@ var RUNTIME_GITIGNORE_BODY = `*
4070
4094
  `;
4071
4095
  async function ensureAiRuntimeLayout(runtimeDir) {
4072
4096
  await mkdir6(runtimeDir, { recursive: true });
4073
- const gi = path11.join(runtimeDir, ".gitignore");
4097
+ const gi = path12.join(runtimeDir, ".gitignore");
4074
4098
  if (!existsSync11(gi)) {
4075
4099
  await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
4076
4100
  }
4077
- const readme = path11.join(runtimeDir, "README.md");
4101
+ const readme = path12.join(runtimeDir, "README.md");
4078
4102
  if (!existsSync11(readme)) {
4079
4103
  await writeFile7(readme, RUNTIME_README_BODY, "utf8");
4080
4104
  }
4081
4105
  }
4082
4106
  async function ensureAiCacheLayout(cacheDir) {
4083
4107
  await mkdir6(cacheDir, { recursive: true });
4084
- const gi = path11.join(cacheDir, ".gitignore");
4108
+ const gi = path12.join(cacheDir, ".gitignore");
4085
4109
  if (!existsSync11(gi)) {
4086
4110
  await writeFile7(gi, "*\n!.gitignore\n", "utf8");
4087
4111
  }
4088
4112
  }
4089
4113
  async function ensureGitignoreEntries(root, patterns) {
4090
4114
  try {
4091
- const gitignorePath = path11.join(root, ".gitignore");
4115
+ const gitignorePath = path12.join(root, ".gitignore");
4092
4116
  let existing = "";
4093
4117
  if (existsSync11(gitignorePath)) {
4094
4118
  existing = await readFile6(gitignorePath, "utf8");
@@ -4105,14 +4129,14 @@ async function ensureGitignoreEntries(root, patterns) {
4105
4129
  // src/commands/install-hooks.ts
4106
4130
  import { mkdir as mkdir8, writeFile as writeFile9, chmod, readFile as readFile8 } from "fs/promises";
4107
4131
  import { existsSync as existsSync13 } from "fs";
4108
- import path13 from "path";
4132
+ import path14 from "path";
4109
4133
  import "commander";
4110
4134
  import { findProjectRoot as findProjectRoot8 } from "@hiveai/core";
4111
4135
 
4112
4136
  // src/utils/claude-hooks.ts
4113
4137
  import { existsSync as existsSync12 } from "fs";
4114
4138
  import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
4115
- import path12 from "path";
4139
+ import path13 from "path";
4116
4140
  var HAIVE_HOOK_TAG = "haive-enforcement";
4117
4141
  var POST_TOOL_USE_GROUP = {
4118
4142
  matcher: "Edit|Write|Bash",
@@ -4208,7 +4232,7 @@ async function installClaudeHooksAtPath(settingsPath) {
4208
4232
  created = true;
4209
4233
  }
4210
4234
  const patched = patchClaudeSettings(raw);
4211
- await mkdir7(path12.dirname(settingsPath), { recursive: true });
4235
+ await mkdir7(path13.dirname(settingsPath), { recursive: true });
4212
4236
  await writeFile8(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
4213
4237
  return { settingsPath, created };
4214
4238
  }
@@ -4224,9 +4248,9 @@ async function uninstallClaudeHooksAtPath(settingsPath) {
4224
4248
  function defaultClaudeSettingsPath(scope, projectRoot) {
4225
4249
  if (scope === "user") {
4226
4250
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4227
- return path12.join(home, ".claude", "settings.json");
4251
+ return path13.join(home, ".claude", "settings.json");
4228
4252
  }
4229
- return path12.join(projectRoot, ".claude", "settings.local.json");
4253
+ return path13.join(projectRoot, ".claude", "settings.local.json");
4230
4254
  }
4231
4255
 
4232
4256
  // src/commands/install-hooks.ts
@@ -4283,18 +4307,18 @@ fi
4283
4307
  ];
4284
4308
  async function installGitHooks(opts) {
4285
4309
  const root = findProjectRoot8(opts.dir);
4286
- const gitDir = path13.join(root, ".git");
4310
+ const gitDir = path14.join(root, ".git");
4287
4311
  if (!existsSync13(gitDir)) {
4288
4312
  ui.error(`No .git directory at ${root}.`);
4289
4313
  process.exitCode = 1;
4290
4314
  return;
4291
4315
  }
4292
- const hooksDir = path13.join(gitDir, "hooks");
4316
+ const hooksDir = path14.join(gitDir, "hooks");
4293
4317
  await mkdir8(hooksDir, { recursive: true });
4294
4318
  let installed = 0;
4295
4319
  let skipped = 0;
4296
4320
  for (const { name, body } of HOOKS) {
4297
- const file = path13.join(hooksDir, name);
4321
+ const file = path14.join(hooksDir, name);
4298
4322
  if (existsSync13(file) && !opts.force) {
4299
4323
  const existing = await readFile8(file, "utf8");
4300
4324
  if (!existing.includes(HOOK_MARKER)) {
@@ -4360,7 +4384,7 @@ function registerInstallHooks(program2) {
4360
4384
  // src/commands/observe.ts
4361
4385
  import { appendFile, mkdir as mkdir9 } from "fs/promises";
4362
4386
  import { existsSync as existsSync14 } from "fs";
4363
- import path14 from "path";
4387
+ import path15 from "path";
4364
4388
  import "commander";
4365
4389
  import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
4366
4390
  var MAX_STDIN_BYTES = 256 * 1024;
@@ -4478,10 +4502,10 @@ function registerObserve(program2) {
4478
4502
  files: extractFiles(payload),
4479
4503
  ...failureHint ? { failure_hint: true } : {}
4480
4504
  };
4481
- const cacheDir = path14.join(paths.haiveDir, ".cache");
4505
+ const cacheDir = path15.join(paths.haiveDir, ".cache");
4482
4506
  await mkdir9(cacheDir, { recursive: true });
4483
4507
  await appendFile(
4484
- path14.join(cacheDir, "observations.jsonl"),
4508
+ path15.join(cacheDir, "observations.jsonl"),
4485
4509
  JSON.stringify(observation) + "\n",
4486
4510
  "utf8"
4487
4511
  );
@@ -4500,7 +4524,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4500
4524
  import { findProjectRoot as findProjectRoot10, resolveHaivePaths as resolveHaivePaths8 } from "@hiveai/core";
4501
4525
  import { mkdir as mkdir10, writeFile as writeFile10 } from "fs/promises";
4502
4526
  import { existsSync as existsSync15 } from "fs";
4503
- import path15 from "path";
4527
+ import path16 from "path";
4504
4528
  import { z } from "zod";
4505
4529
  import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
4506
4530
  import { existsSync as existsSync22 } from "fs";
@@ -4670,6 +4694,7 @@ import path82 from "path";
4670
4694
  import { execSync } from "child_process";
4671
4695
  import { readFile as readFile52, writeFile as writeFile13 } from "fs/promises";
4672
4696
  import { existsSync as existsSync21 } from "fs";
4697
+ import path112 from "path";
4673
4698
  import {
4674
4699
  allocateBudget,
4675
4700
  briefingProofLine,
@@ -4729,7 +4754,7 @@ import { z as z23 } from "zod";
4729
4754
  import { z as z24 } from "zod";
4730
4755
  import { existsSync as existsSync24 } from "fs";
4731
4756
  import { spawn } from "child_process";
4732
- import path112 from "path";
4757
+ import path122 from "path";
4733
4758
  import {
4734
4759
  deriveConfidence as deriveConfidence5,
4735
4760
  getUsage as getUsage7,
@@ -4788,7 +4813,7 @@ import { z as z29 } from "zod";
4788
4813
  import { z as z30 } from "zod";
4789
4814
  import { mkdir as mkdir82, writeFile as writeFile14 } from "fs/promises";
4790
4815
  import { existsSync as existsSync29 } from "fs";
4791
- import path122 from "path";
4816
+ import path132 from "path";
4792
4817
  import { execSync as execSync2 } from "child_process";
4793
4818
  import {
4794
4819
  buildFrontmatter as buildFrontmatter5,
@@ -4834,14 +4859,14 @@ var BootstrapProjectSaveInputSchema = {
4834
4859
  overwrite: z.boolean().default(false).describe("Overwrite an existing file instead of failing")
4835
4860
  };
4836
4861
  async function bootstrapProjectSave(input, ctx) {
4837
- const target = input.module ? path15.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4862
+ const target = input.module ? path16.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4838
4863
  const exists = existsSync15(target);
4839
4864
  if (exists && !input.overwrite) {
4840
4865
  throw new Error(
4841
4866
  `${target} already exists. Pass overwrite=true to replace it.`
4842
4867
  );
4843
4868
  }
4844
- await mkdir10(path15.dirname(target), { recursive: true });
4869
+ await mkdir10(path16.dirname(target), { recursive: true });
4845
4870
  await writeFile10(target, input.content, "utf8");
4846
4871
  return {
4847
4872
  file_path: target,
@@ -6579,6 +6604,7 @@ async function getBriefing(input, ctx) {
6579
6604
  const topSymbols = Object.entries(codeMap.files).flatMap(
6580
6605
  ([fp, entry]) => entry.exports.slice(0, 3).map((e) => `${e.name} (${fp.split("/").slice(-2).join("/")})`)
6581
6606
  ).slice(0, 15).join(", ");
6607
+ const commands = await detectRunCommands(ctx.paths.root);
6582
6608
  projectContext = `# Project context (auto-generated by hAIve)
6583
6609
 
6584
6610
  > \u26A0 This is a minimal auto-generated context based on the code-map. Invoke the \`bootstrap_project\` MCP prompt to replace it with a full analysis.
@@ -6588,7 +6614,10 @@ async function getBriefing(input, ctx) {
6588
6614
  - **Main file types:** ${topExts}
6589
6615
  - **Generated at:** ${codeMap.generated_at}
6590
6616
 
6591
- ## Key exports (sample)
6617
+ ` + (commands ? `## Commands
6618
+ ${commands}
6619
+
6620
+ ` : "") + `## Key exports (sample)
6592
6621
  ` + topSymbols + "\n";
6593
6622
  autoContextGenerated = true;
6594
6623
  setupWarnings.push(
@@ -6868,6 +6897,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
6868
6897
  }
6869
6898
  };
6870
6899
  }
6900
+ async function detectRunCommands(root) {
6901
+ const pkgPath = path112.join(root, "package.json");
6902
+ if (!existsSync21(pkgPath)) return null;
6903
+ try {
6904
+ const pkg = JSON.parse(await readFile52(pkgPath, "utf8"));
6905
+ const scripts = pkg.scripts ?? {};
6906
+ const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
6907
+ const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
6908
+ return lines.length > 0 ? lines.join("\n") : null;
6909
+ } catch {
6910
+ return null;
6911
+ }
6912
+ }
6871
6913
  var CodeMapInputSchema = {
6872
6914
  file: z20.string().optional().describe("Filter to files whose path contains this substring"),
6873
6915
  symbol: z20.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
@@ -7091,7 +7133,7 @@ var WhyThisFileInputSchema = {
7091
7133
  memory_limit: z25.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
7092
7134
  };
7093
7135
  async function whyThisFile(input, ctx) {
7094
- const fileExists = existsSync24(path112.join(ctx.paths.root, input.path));
7136
+ const fileExists = existsSync24(path122.join(ctx.paths.root, input.path));
7095
7137
  const [commits, memories, codeMap] = await Promise.all([
7096
7138
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
7097
7139
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -8123,13 +8165,13 @@ async function patternDetect(input, ctx) {
8123
8165
  try {
8124
8166
  const changedFiles = gitChangedFiles(ctx.paths.root, input.since_days);
8125
8167
  const configFiles = changedFiles.filter(
8126
- (f) => CONFIG_PATTERNS.some((p) => path122.basename(f.toLowerCase()).includes(p))
8168
+ (f) => CONFIG_PATTERNS.some((p) => path132.basename(f.toLowerCase()).includes(p))
8127
8169
  );
8128
8170
  for (const file of configFiles.slice(0, 5)) {
8129
8171
  const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
8130
8172
  if (!diff) continue;
8131
- const parentDir = path122.basename(path122.dirname(file));
8132
- const baseName = path122.basename(file).replace(/\.[^.]+$/, "");
8173
+ const parentDir = path132.basename(path132.dirname(file));
8174
+ const baseName = path132.basename(file).replace(/\.[^.]+$/, "");
8133
8175
  const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
8134
8176
  matches.push({
8135
8177
  kind: "config_change",
@@ -8193,7 +8235,7 @@ async function patternDetect(input, ctx) {
8193
8235
  for (const [p, { count, tools }] of pathCounts) {
8194
8236
  if (count < HOT_FILE_MIN) continue;
8195
8237
  if (tools.has("mem_tried") || tools.has("mem_observe")) continue;
8196
- if (CONFIG_PATTERNS.some((cp2) => path122.basename(p).includes(cp2))) continue;
8238
+ if (CONFIG_PATTERNS.some((cp2) => path132.basename(p).includes(cp2))) continue;
8197
8239
  const slug = p.replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").slice(0, 40);
8198
8240
  matches.push({
8199
8241
  kind: "hot_file",
@@ -8240,7 +8282,7 @@ async function patternDetect(input, ctx) {
8240
8282
  void 0
8241
8283
  );
8242
8284
  if (existsSync29(file)) continue;
8243
- await mkdir82(path122.dirname(file), { recursive: true });
8285
+ await mkdir82(path132.dirname(file), { recursive: true });
8244
8286
  await writeFile14(
8245
8287
  file,
8246
8288
  serializeMemory12({ frontmatter: fm, body: match.proposed_body }),
@@ -8654,7 +8696,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
8654
8696
  };
8655
8697
  }
8656
8698
  var SERVER_NAME = "haive";
8657
- var SERVER_VERSION = "0.23.0";
8699
+ var SERVER_VERSION = "0.25.0";
8658
8700
  function jsonResult(data) {
8659
8701
  return {
8660
8702
  content: [
@@ -9688,7 +9730,7 @@ function registerMcp(program2) {
9688
9730
  import { spawnSync as spawnSync4 } from "child_process";
9689
9731
  import { readFile as readFile10, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
9690
9732
  import { existsSync as existsSync33 } from "fs";
9691
- import path16 from "path";
9733
+ import path17 from "path";
9692
9734
  import "commander";
9693
9735
  import {
9694
9736
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
@@ -9882,10 +9924,10 @@ function registerSync(program2) {
9882
9924
  const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
9883
9925
  let bridgeTargets;
9884
9926
  if (opts.bridgeFile) {
9885
- bridgeTargets = [path16.resolve(opts.bridgeFile)];
9927
+ bridgeTargets = [path17.resolve(opts.bridgeFile)];
9886
9928
  } else {
9887
- const agentsMd = path16.join(root, "AGENTS.md");
9888
- bridgeTargets = [path16.join(root, "CLAUDE.md")];
9929
+ const agentsMd = path17.join(root, "AGENTS.md");
9930
+ bridgeTargets = [path17.join(root, "CLAUDE.md")];
9889
9931
  if (existsSync33(agentsMd)) bridgeTargets.push(agentsMd);
9890
9932
  }
9891
9933
  for (const bridgeFile of bridgeTargets) {
@@ -10017,10 +10059,10 @@ Wait for **explicit confirmation** before acting.
10017
10059
  topic: `dep-bump-${slugParts}`
10018
10060
  });
10019
10061
  if (!dryRun) {
10020
- const teamDir = path16.join(paths.memoriesDir, "team");
10062
+ const teamDir = path17.join(paths.memoriesDir, "team");
10021
10063
  await mkdir11(teamDir, { recursive: true });
10022
10064
  await writeFile15(
10023
- path16.join(teamDir, `${fm.id}.md`),
10065
+ path17.join(teamDir, `${fm.id}.md`),
10024
10066
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
10025
10067
  "utf8"
10026
10068
  );
@@ -10086,10 +10128,10 @@ Wait for **explicit confirmation** before acting.
10086
10128
  topic: `contract-breaking-${diff.contract}`
10087
10129
  });
10088
10130
  if (!dryRun) {
10089
- const teamDir = path16.join(paths.memoriesDir, "team");
10131
+ const teamDir = path17.join(paths.memoriesDir, "team");
10090
10132
  await mkdir11(teamDir, { recursive: true });
10091
10133
  await writeFile15(
10092
- path16.join(teamDir, `${fm.id}.md`),
10134
+ path17.join(teamDir, `${fm.id}.md`),
10093
10135
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
10094
10136
  "utf8"
10095
10137
  );
@@ -10212,11 +10254,11 @@ ${BRIDGE_END}`;
10212
10254
  const startIdx = existing.indexOf(BRIDGE_START);
10213
10255
  const endIdx = existing.indexOf(BRIDGE_END);
10214
10256
  if (startIdx !== -1 && endIdx === -1) {
10215
- ui.warn(`${path16.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
10257
+ ui.warn(`${path17.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
10216
10258
  return;
10217
10259
  }
10218
10260
  if (startIdx === -1 && endIdx !== -1) {
10219
- ui.warn(`${path16.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
10261
+ ui.warn(`${path17.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
10220
10262
  return;
10221
10263
  }
10222
10264
  let updated;
@@ -10224,14 +10266,14 @@ ${BRIDGE_END}`;
10224
10266
  updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
10225
10267
  } else {
10226
10268
  if (!fileExists && !quiet) {
10227
- ui.info(`Creating ${path16.relative(root, bridgeFile)} with haive memory block.`);
10269
+ ui.info(`Creating ${path17.relative(root, bridgeFile)} with haive memory block.`);
10228
10270
  }
10229
10271
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
10230
10272
  }
10231
10273
  await writeFile15(bridgeFile, updated, "utf8");
10232
10274
  if (!quiet) {
10233
10275
  console.log(
10234
- ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path16.relative(root, bridgeFile)}`)
10276
+ ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path17.relative(root, bridgeFile)}`)
10235
10277
  );
10236
10278
  }
10237
10279
  }
@@ -10258,7 +10300,7 @@ function collectSinceChanges(root, ref) {
10258
10300
  import { createHash as createHash2 } from "crypto";
10259
10301
  import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile16 } from "fs/promises";
10260
10302
  import { existsSync as existsSync34 } from "fs";
10261
- import path17 from "path";
10303
+ import path18 from "path";
10262
10304
  import "commander";
10263
10305
  import {
10264
10306
  buildFrontmatter as buildFrontmatter7,
@@ -10316,7 +10358,7 @@ function registerMemoryAdd(memory2) {
10316
10358
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
10317
10359
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
10318
10360
  if (anchorPaths.length > 0) {
10319
- const missing = anchorPaths.filter((p) => !existsSync34(path17.resolve(root, p)));
10361
+ const missing = anchorPaths.filter((p) => !existsSync34(path18.resolve(root, p)));
10320
10362
  if (missing.length > 0) {
10321
10363
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
10322
10364
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -10380,7 +10422,7 @@ TODO \u2014 write the memory body.
10380
10422
  const suggestedSensor = !newFrontmatter.sensor ? suggestSensorForCliMemory(opts.type, body, newFrontmatter.anchor.paths) : null;
10381
10423
  if (suggestedSensor) newFrontmatter.sensor = suggestedSensor;
10382
10424
  await writeFile16(topicMatch.filePath, serializeMemory14({ frontmatter: newFrontmatter, body }), "utf8");
10383
- ui.success(`Updated (topic upsert) ${path17.relative(root, topicMatch.filePath)}`);
10425
+ ui.success(`Updated (topic upsert) ${path18.relative(root, topicMatch.filePath)}`);
10384
10426
  ui.info(`id=${fm.id} revision=${revisionCount}`);
10385
10427
  if (suggestedSensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(suggestedSensor.pattern)}`);
10386
10428
  await runPostMemoryAutopilot(root, paths, config);
@@ -10404,7 +10446,7 @@ TODO \u2014 write the memory body.
10404
10446
  activation
10405
10447
  });
10406
10448
  const file = memoryFilePath7(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
10407
- await mkdir12(path17.dirname(file), { recursive: true });
10449
+ await mkdir12(path18.dirname(file), { recursive: true });
10408
10450
  if (existsSync34(file)) {
10409
10451
  ui.error(`Memory already exists at ${file}`);
10410
10452
  process.exitCode = 1;
@@ -10423,7 +10465,7 @@ TODO \u2014 write the memory body.
10423
10465
  }
10424
10466
  }
10425
10467
  await writeFile16(file, serializeMemory14({ frontmatter, body }), "utf8");
10426
- ui.success(`Created ${path17.relative(root, file)}`);
10468
+ ui.success(`Created ${path18.relative(root, file)}`);
10427
10469
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
10428
10470
  if (frontmatter.sensor?.autogen) {
10429
10471
  ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(frontmatter.sensor.pattern)}`);
@@ -10503,7 +10545,7 @@ function slugify(value) {
10503
10545
 
10504
10546
  // src/commands/memory-list.ts
10505
10547
  import { existsSync as existsSync35 } from "fs";
10506
- import path18 from "path";
10548
+ import path19 from "path";
10507
10549
  import "commander";
10508
10550
  import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
10509
10551
 
@@ -10556,7 +10598,7 @@ function registerMemoryList(memory2) {
10556
10598
  );
10557
10599
  const title = mem.body.match(/^#\s+(.+)$/m)?.[1]?.trim();
10558
10600
  if (title && title !== fm.id) console.log(` ${title}`);
10559
- console.log(` ${ui.dim(path18.relative(root, filePath))}`);
10601
+ console.log(` ${ui.dim(path19.relative(root, filePath))}`);
10560
10602
  }
10561
10603
  const totalLabel = clipped > 0 ? `
10562
10604
  ${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
@@ -10595,7 +10637,7 @@ function matchesFilters(loaded, opts) {
10595
10637
  // src/commands/memory-promote.ts
10596
10638
  import { mkdir as mkdir13, unlink as unlink2, writeFile as writeFile17 } from "fs/promises";
10597
10639
  import { existsSync as existsSync36 } from "fs";
10598
- import path19 from "path";
10640
+ import path20 from "path";
10599
10641
  import "commander";
10600
10642
  import {
10601
10643
  findProjectRoot as findProjectRoot15,
@@ -10642,11 +10684,11 @@ function registerMemoryPromote(memory2) {
10642
10684
  body: found.memory.body
10643
10685
  };
10644
10686
  const newPath = memoryFilePath8(paths, "team", updated.frontmatter.id);
10645
- await mkdir13(path19.dirname(newPath), { recursive: true });
10687
+ await mkdir13(path20.dirname(newPath), { recursive: true });
10646
10688
  await writeFile17(newPath, serializeMemory15(updated), "utf8");
10647
10689
  await unlink2(found.filePath);
10648
10690
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
10649
- ui.info(`Now at ${path19.relative(root, newPath)}`);
10691
+ ui.info(`Now at ${path20.relative(root, newPath)}`);
10650
10692
  console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
10651
10693
  });
10652
10694
  }
@@ -10654,7 +10696,7 @@ function registerMemoryPromote(memory2) {
10654
10696
  // src/commands/memory-approve.ts
10655
10697
  import { existsSync as existsSync37 } from "fs";
10656
10698
  import { writeFile as writeFile18 } from "fs/promises";
10657
- import path20 from "path";
10699
+ import path21 from "path";
10658
10700
  import "commander";
10659
10701
  import {
10660
10702
  findProjectRoot as findProjectRoot16,
@@ -10718,14 +10760,14 @@ function registerMemoryApprove(memory2) {
10718
10760
  };
10719
10761
  await writeFile18(found.filePath, serializeMemory16(next), "utf8");
10720
10762
  ui.success(`Approved ${id} (status=validated)`);
10721
- ui.info(path20.relative(root, found.filePath));
10763
+ ui.info(path21.relative(root, found.filePath));
10722
10764
  });
10723
10765
  }
10724
10766
 
10725
10767
  // src/commands/memory-update.ts
10726
10768
  import { readFile as readFile12, writeFile as writeFile19 } from "fs/promises";
10727
10769
  import { existsSync as existsSync38 } from "fs";
10728
- import path21 from "path";
10770
+ import path23 from "path";
10729
10771
  import "commander";
10730
10772
  import {
10731
10773
  findProjectRoot as findProjectRoot17,
@@ -10804,7 +10846,7 @@ function registerMemoryUpdate(memory2) {
10804
10846
  serializeMemory17({ frontmatter: newFrontmatter, body: newBody }),
10805
10847
  "utf8"
10806
10848
  );
10807
- ui.success(`Updated ${path21.relative(root, loaded.filePath)}`);
10849
+ ui.success(`Updated ${path23.relative(root, loaded.filePath)}`);
10808
10850
  ui.info(`fields: ${updated.join(", ")}`);
10809
10851
  });
10810
10852
  }
@@ -10825,7 +10867,7 @@ function parseCsv3(value) {
10825
10867
  // src/commands/memory-auto-promote.ts
10826
10868
  import { writeFile as writeFile20 } from "fs/promises";
10827
10869
  import { existsSync as existsSync39 } from "fs";
10828
- import path23 from "path";
10870
+ import path24 from "path";
10829
10871
  import "commander";
10830
10872
  import {
10831
10873
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
@@ -10870,7 +10912,7 @@ function registerMemoryAutoPromote(memory2) {
10870
10912
  console.log(
10871
10913
  `${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
10872
10914
  );
10873
- console.log(` ${ui.dim(path23.relative(root, filePath))}`);
10915
+ console.log(` ${ui.dim(path24.relative(root, filePath))}`);
10874
10916
  if (opts.apply) {
10875
10917
  const next = {
10876
10918
  frontmatter: { ...mem.frontmatter, status: "validated" },
@@ -10889,7 +10931,7 @@ function registerMemoryAutoPromote(memory2) {
10889
10931
  import { spawn as spawn3 } from "child_process";
10890
10932
  import { existsSync as existsSync40 } from "fs";
10891
10933
  import { readFile as readFile13 } from "fs/promises";
10892
- import path24 from "path";
10934
+ import path25 from "path";
10893
10935
  import "commander";
10894
10936
  import {
10895
10937
  findProjectRoot as findProjectRoot19,
@@ -10913,7 +10955,7 @@ function registerMemoryEdit(memory2) {
10913
10955
  return;
10914
10956
  }
10915
10957
  const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
10916
- ui.info(`Opening ${path24.relative(root, found.filePath)} with ${editor}\u2026`);
10958
+ ui.info(`Opening ${path25.relative(root, found.filePath)} with ${editor}\u2026`);
10917
10959
  const code = await runEditor(editor, found.filePath);
10918
10960
  if (code !== 0) {
10919
10961
  ui.warn(`Editor exited with status ${code}.`);
@@ -10941,7 +10983,7 @@ function runEditor(editor, file) {
10941
10983
 
10942
10984
  // src/commands/memory-for-files.ts
10943
10985
  import { existsSync as existsSync41 } from "fs";
10944
- import path25 from "path";
10986
+ import path26 from "path";
10945
10987
  import "commander";
10946
10988
  import {
10947
10989
  deriveConfidence as deriveConfidence9,
@@ -11063,13 +11105,13 @@ function printGroup(root, label, loaded, usage) {
11063
11105
  const u = getUsage13(usage, fm.id);
11064
11106
  const conf = deriveConfidence9(fm, u);
11065
11107
  console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
11066
- console.log(` ${ui.dim(path25.relative(root, filePath))}`);
11108
+ console.log(` ${ui.dim(path26.relative(root, filePath))}`);
11067
11109
  }
11068
11110
  }
11069
11111
 
11070
11112
  // src/commands/memory-hot.ts
11071
11113
  import { existsSync as existsSync43 } from "fs";
11072
- import path26 from "path";
11114
+ import path27 from "path";
11073
11115
  import "commander";
11074
11116
  import {
11075
11117
  findProjectRoot as findProjectRoot21,
@@ -11111,7 +11153,7 @@ function registerMemoryHot(memory2) {
11111
11153
  console.log(
11112
11154
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(fm.status)} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
11113
11155
  );
11114
- console.log(` ${ui.dim(path26.relative(root, filePath))}`);
11156
+ console.log(` ${ui.dim(path27.relative(root, filePath))}`);
11115
11157
  }
11116
11158
  ui.info(
11117
11159
  `${candidates.length} hot (read \u2265${threshold}\xD7) \u2014 agents rely on these; promote with \`haive memory promote <id>\`.
@@ -11123,7 +11165,7 @@ function registerMemoryHot(memory2) {
11123
11165
  // src/commands/memory-tried.ts
11124
11166
  import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
11125
11167
  import { existsSync as existsSync44 } from "fs";
11126
- import path27 from "path";
11168
+ import path28 from "path";
11127
11169
  import "commander";
11128
11170
  import {
11129
11171
  buildFrontmatter as buildFrontmatter8,
@@ -11179,14 +11221,14 @@ function registerMemoryTried(memory2) {
11179
11221
  frontmatter.sensor = sensor;
11180
11222
  }
11181
11223
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
11182
- await mkdir14(path27.dirname(file), { recursive: true });
11224
+ await mkdir14(path28.dirname(file), { recursive: true });
11183
11225
  if (existsSync44(file)) {
11184
11226
  ui.error(`Memory already exists at ${file}`);
11185
11227
  process.exitCode = 1;
11186
11228
  return;
11187
11229
  }
11188
11230
  await writeFile21(file, serializeMemory19({ frontmatter, body }), "utf8");
11189
- ui.success(`Recorded: ${path27.relative(root, file)}`);
11231
+ ui.success(`Recorded: ${path28.relative(root, file)}`);
11190
11232
  ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
11191
11233
  if (sensor) {
11192
11234
  ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(sensor.pattern)}`);
@@ -11205,7 +11247,7 @@ function parseCsv4(value) {
11205
11247
  // src/commands/memory-seed.ts
11206
11248
  import { readFile as readFile14 } from "fs/promises";
11207
11249
  import { existsSync as existsSync45 } from "fs";
11208
- import path28 from "path";
11250
+ import path29 from "path";
11209
11251
  import "commander";
11210
11252
  import {
11211
11253
  findProjectRoot as findProjectRoot23,
@@ -11214,7 +11256,7 @@ import {
11214
11256
  } from "@hiveai/core";
11215
11257
  async function readDependencyMap(root) {
11216
11258
  try {
11217
- const raw = await readFile14(path28.join(root, "package.json"), "utf8");
11259
+ const raw = await readFile14(path29.join(root, "package.json"), "utf8");
11218
11260
  const pkg = JSON.parse(raw);
11219
11261
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
11220
11262
  } catch {
@@ -11301,7 +11343,7 @@ function registerMemorySeed(memory2) {
11301
11343
 
11302
11344
  // src/commands/memory-pending.ts
11303
11345
  import { existsSync as existsSync46 } from "fs";
11304
- import path29 from "path";
11346
+ import path30 from "path";
11305
11347
  import "commander";
11306
11348
  import {
11307
11349
  findProjectRoot as findProjectRoot24,
@@ -11347,7 +11389,7 @@ function registerMemoryPending(memory2) {
11347
11389
  console.log(
11348
11390
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
11349
11391
  );
11350
- console.log(` ${ui.dim(path29.relative(root, filePath))}`);
11392
+ console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11351
11393
  }
11352
11394
  if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
11353
11395
  console.log();
@@ -11362,7 +11404,7 @@ function registerMemoryPending(memory2) {
11362
11404
  console.log(
11363
11405
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
11364
11406
  );
11365
- console.log(` ${ui.dim(path29.relative(root, filePath))}`);
11407
+ console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11366
11408
  }
11367
11409
  console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
11368
11410
  }
@@ -11372,7 +11414,7 @@ function registerMemoryPending(memory2) {
11372
11414
 
11373
11415
  // src/commands/memory-query.ts
11374
11416
  import { existsSync as existsSync47 } from "fs";
11375
- import path30 from "path";
11417
+ import path31 from "path";
11376
11418
  import "commander";
11377
11419
  import {
11378
11420
  extractSnippet as extractSnippet2,
@@ -11429,7 +11471,7 @@ function registerMemoryQuery(memory2) {
11429
11471
  const fm = mem.frontmatter;
11430
11472
  const statusBadge = ui.statusBadge(fm.status);
11431
11473
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
11432
- console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11474
+ console.log(` ${ui.dim(path31.relative(root, filePath))}`);
11433
11475
  const snippet = extractSnippet2(mem.body, snippetNeedle);
11434
11476
  if (snippet) console.log(` ${snippet}`);
11435
11477
  }
@@ -11499,7 +11541,7 @@ function registerMemoryReject(memory2) {
11499
11541
  // src/commands/memory-rm.ts
11500
11542
  import { existsSync as existsSync49 } from "fs";
11501
11543
  import { unlink as unlink3 } from "fs/promises";
11502
- import path31 from "path";
11544
+ import path33 from "path";
11503
11545
  import { createInterface as createInterface2 } from "readline/promises";
11504
11546
  import "commander";
11505
11547
  import {
@@ -11524,7 +11566,7 @@ function registerMemoryRm(memory2) {
11524
11566
  process.exitCode = 1;
11525
11567
  return;
11526
11568
  }
11527
- const rel = path31.relative(root, found.filePath);
11569
+ const rel = path33.relative(root, found.filePath);
11528
11570
  if (!opts.yes) {
11529
11571
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
11530
11572
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
@@ -11550,7 +11592,7 @@ function registerMemoryRm(memory2) {
11550
11592
  // src/commands/memory-show.ts
11551
11593
  import { existsSync as existsSync50 } from "fs";
11552
11594
  import { readFile as readFile15 } from "fs/promises";
11553
- import path33 from "path";
11595
+ import path34 from "path";
11554
11596
  import "commander";
11555
11597
  import {
11556
11598
  deriveConfidence as deriveConfidence10,
@@ -11592,7 +11634,7 @@ function registerMemoryShow(memory2) {
11592
11634
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
11593
11635
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
11594
11636
  console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
11595
- console.log(`${ui.dim("file:")} ${path33.relative(root, found.filePath)}`);
11637
+ console.log(`${ui.dim("file:")} ${path34.relative(root, found.filePath)}`);
11596
11638
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
11597
11639
  console.log(ui.dim("anchor:"));
11598
11640
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -11608,7 +11650,7 @@ function registerMemoryShow(memory2) {
11608
11650
 
11609
11651
  // src/commands/memory-stats.ts
11610
11652
  import { existsSync as existsSync51 } from "fs";
11611
- import path34 from "path";
11653
+ import path35 from "path";
11612
11654
  import "commander";
11613
11655
  import {
11614
11656
  deriveConfidence as deriveConfidence11,
@@ -11646,7 +11688,7 @@ function registerMemoryStats(memory2) {
11646
11688
  console.log(
11647
11689
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
11648
11690
  );
11649
- console.log(` ${ui.dim(path34.relative(root, filePath))}`);
11691
+ console.log(` ${ui.dim(path35.relative(root, filePath))}`);
11650
11692
  }
11651
11693
  });
11652
11694
  }
@@ -11809,7 +11851,7 @@ function registerMemoryFeedback(memory2) {
11809
11851
  // src/commands/memory-verify.ts
11810
11852
  import { writeFile as writeFile25 } from "fs/promises";
11811
11853
  import { existsSync as existsSync55 } from "fs";
11812
- import path35 from "path";
11854
+ import path36 from "path";
11813
11855
  import "commander";
11814
11856
  import {
11815
11857
  findProjectRoot as findProjectRoot32,
@@ -11851,7 +11893,7 @@ function registerMemoryVerify(memory2) {
11851
11893
  for (const { memory: mem, filePath } of targets) {
11852
11894
  const result = await verifyAnchor3(mem, { projectRoot: root });
11853
11895
  const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
11854
- const rel = path35.relative(root, filePath);
11896
+ const rel = path36.relative(root, filePath);
11855
11897
  if (!isAnchored) {
11856
11898
  anchorlessIds.push(mem.frontmatter.id);
11857
11899
  entries.push({ id: mem.frontmatter.id, status: "anchorless", path: rel });
@@ -11996,7 +12038,7 @@ function registerMemoryImport(memory2) {
11996
12038
  // src/commands/memory-import-changelog.ts
11997
12039
  import { existsSync as existsSync57 } from "fs";
11998
12040
  import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile26 } from "fs/promises";
11999
- import path36 from "path";
12041
+ import path37 from "path";
12000
12042
  import "commander";
12001
12043
  import {
12002
12044
  buildFrontmatter as buildFrontmatter9,
@@ -12068,7 +12110,7 @@ function registerMemoryImportChangelog(memory2) {
12068
12110
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
12069
12111
  const root = findProjectRoot34(opts.dir);
12070
12112
  const paths = resolveHaivePaths31(root);
12071
- const changelogPath = path36.resolve(root, opts.fromChangelog);
12113
+ const changelogPath = path37.resolve(root, opts.fromChangelog);
12072
12114
  if (!existsSync57(changelogPath)) {
12073
12115
  ui.error(`CHANGELOG not found: ${changelogPath}`);
12074
12116
  process.exitCode = 1;
@@ -12089,9 +12131,9 @@ function registerMemoryImportChangelog(memory2) {
12089
12131
  entries = entries.filter((e) => requested.includes(e.version));
12090
12132
  }
12091
12133
  }
12092
- const pkgName = opts.package ?? path36.basename(path36.dirname(changelogPath));
12134
+ const pkgName = opts.package ?? path37.basename(path37.dirname(changelogPath));
12093
12135
  const scope = opts.scope ?? "team";
12094
- const teamDir = path36.join(paths.memoriesDir, scope);
12136
+ const teamDir = path37.join(paths.memoriesDir, scope);
12095
12137
  await mkdir15(teamDir, { recursive: true });
12096
12138
  let saved = 0;
12097
12139
  for (const entry of entries) {
@@ -12114,7 +12156,7 @@ function registerMemoryImportChangelog(memory2) {
12114
12156
  lines.push("");
12115
12157
  }
12116
12158
  lines.push(
12117
- `**Source:** \`${path36.relative(root, changelogPath)}\`
12159
+ `**Source:** \`${path37.relative(root, changelogPath)}\`
12118
12160
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
12119
12161
  );
12120
12162
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -12129,11 +12171,11 @@ function registerMemoryImportChangelog(memory2) {
12129
12171
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
12130
12172
  `v${entry.version}`
12131
12173
  ],
12132
- paths: [path36.relative(root, changelogPath)],
12174
+ paths: [path37.relative(root, changelogPath)],
12133
12175
  topic: `changelog-${pkgName}-${entry.version}`
12134
12176
  });
12135
12177
  await writeFile26(
12136
- path36.join(teamDir, `${fm.id}.md`),
12178
+ path37.join(teamDir, `${fm.id}.md`),
12137
12179
  serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
12138
12180
  "utf8"
12139
12181
  );
@@ -12158,7 +12200,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
12158
12200
  // src/commands/memory-digest.ts
12159
12201
  import { existsSync as existsSync58 } from "fs";
12160
12202
  import { writeFile as writeFile27 } from "fs/promises";
12161
- import path37 from "path";
12203
+ import path38 from "path";
12162
12204
  import "commander";
12163
12205
  import {
12164
12206
  deriveConfidence as deriveConfidence12,
@@ -12253,7 +12295,7 @@ function registerMemoryDigest(program2) {
12253
12295
  );
12254
12296
  const digest = lines.join("\n");
12255
12297
  if (opts.out) {
12256
- const outPath = path37.resolve(process.cwd(), opts.out);
12298
+ const outPath = path38.resolve(process.cwd(), opts.out);
12257
12299
  await writeFile27(outPath, digest, "utf8");
12258
12300
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
12259
12301
  } else {
@@ -12266,7 +12308,7 @@ function registerMemoryDigest(program2) {
12266
12308
  import { writeFile as writeFile28, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
12267
12309
  import { existsSync as existsSync59 } from "fs";
12268
12310
  import { spawn as spawn4 } from "child_process";
12269
- import path38 from "path";
12311
+ import path39 from "path";
12270
12312
  import "commander";
12271
12313
  import {
12272
12314
  buildFrontmatter as buildFrontmatter10,
@@ -12281,7 +12323,7 @@ import {
12281
12323
  summarizeCaughtForYou
12282
12324
  } from "@hiveai/core";
12283
12325
  async function buildAutoRecap(paths) {
12284
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12326
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12285
12327
  if (!existsSync59(obsFile)) return await buildGitAutoRecap(paths);
12286
12328
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12287
12329
  if (!raw.trim()) return await buildGitAutoRecap(paths);
@@ -12450,7 +12492,7 @@ function runGit(cwd, args) {
12450
12492
  });
12451
12493
  }
12452
12494
  async function observationStart(paths) {
12453
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12495
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12454
12496
  if (!existsSync59(obsFile)) return null;
12455
12497
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12456
12498
  let first = null;
@@ -12539,14 +12581,14 @@ function registerSessionEnd(session2) {
12539
12581
  });
12540
12582
  const topic = recapTopic2(scope, opts.module);
12541
12583
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
12542
- const missingPaths = filesTouched.filter((p) => !existsSync59(path38.resolve(root, p)));
12584
+ const missingPaths = filesTouched.filter((p) => !existsSync59(path39.resolve(root, p)));
12543
12585
  if (missingPaths.length > 0 && !opts.quiet) {
12544
12586
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
12545
12587
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
12546
12588
  }
12547
12589
  const cleanupObservations = async () => {
12548
12590
  if (!opts.auto) return;
12549
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12591
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12550
12592
  if (existsSync59(obsFile)) await rm2(obsFile).catch(() => {
12551
12593
  });
12552
12594
  };
@@ -12571,7 +12613,7 @@ function registerSessionEnd(session2) {
12571
12613
  await cleanupObservations();
12572
12614
  if (!opts.quiet) {
12573
12615
  ui.success(`Session recap updated (revision #${revisionCount})`);
12574
- ui.info(`id=${fm.id} file=${path38.relative(root, topicMatch.filePath)}`);
12616
+ ui.info(`id=${fm.id} file=${path39.relative(root, topicMatch.filePath)}`);
12575
12617
  await printCaughtForYou(paths, caughtSince, opts.quiet);
12576
12618
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
12577
12619
  }
@@ -12589,12 +12631,12 @@ function registerSessionEnd(session2) {
12589
12631
  status: "validated"
12590
12632
  });
12591
12633
  const file = memoryFilePath10(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
12592
- await mkdir16(path38.dirname(file), { recursive: true });
12634
+ await mkdir16(path39.dirname(file), { recursive: true });
12593
12635
  await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
12594
12636
  await cleanupObservations();
12595
12637
  if (!opts.quiet) {
12596
12638
  ui.success(`Session recap created`);
12597
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path38.relative(root, file)}`);
12639
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path39.relative(root, file)}`);
12598
12640
  await printCaughtForYou(paths, caughtSince, opts.quiet);
12599
12641
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
12600
12642
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
@@ -12607,8 +12649,8 @@ function parseCsv5(value) {
12607
12649
  }
12608
12650
  function normalizeAnchorPath(root, filePath) {
12609
12651
  if (!filePath) return filePath;
12610
- if (!path38.isAbsolute(filePath)) return filePath;
12611
- const rel = path38.relative(root, filePath);
12652
+ if (!path39.isAbsolute(filePath)) return filePath;
12653
+ const rel = path39.relative(root, filePath);
12612
12654
  if (rel.startsWith("..")) return filePath;
12613
12655
  return rel;
12614
12656
  }
@@ -12616,7 +12658,7 @@ function normalizeAnchorPath(root, filePath) {
12616
12658
  // src/commands/snapshot.ts
12617
12659
  import { existsSync as existsSync60 } from "fs";
12618
12660
  import { readdir as readdir4 } from "fs/promises";
12619
- import path39 from "path";
12661
+ import path40 from "path";
12620
12662
  import "commander";
12621
12663
  import {
12622
12664
  diffContract,
@@ -12655,7 +12697,7 @@ function registerSnapshot(program2) {
12655
12697
  return;
12656
12698
  }
12657
12699
  if (opts.list) {
12658
- const contractsDir = path39.join(paths.haiveDir, "contracts");
12700
+ const contractsDir = path40.join(paths.haiveDir, "contracts");
12659
12701
  if (!existsSync60(contractsDir)) {
12660
12702
  console.log(ui.dim("No contract snapshots found."));
12661
12703
  return;
@@ -12711,7 +12753,7 @@ function registerSnapshot(program2) {
12711
12753
  return;
12712
12754
  }
12713
12755
  const contractPath = opts.contract;
12714
- const name = opts.name ?? path39.basename(contractPath, path39.extname(contractPath));
12756
+ const name = opts.name ?? path40.basename(contractPath, path40.extname(contractPath));
12715
12757
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
12716
12758
  const contract = { name, path: contractPath, format };
12717
12759
  try {
@@ -12766,8 +12808,8 @@ async function runDiff(root, haiveDir, contract) {
12766
12808
  }
12767
12809
  }
12768
12810
  function detectFormat(filePath) {
12769
- const ext = path39.extname(filePath).toLowerCase();
12770
- const base = path39.basename(filePath).toLowerCase();
12811
+ const ext = path40.extname(filePath).toLowerCase();
12812
+ const base = path40.basename(filePath).toLowerCase();
12771
12813
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
12772
12814
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
12773
12815
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -12782,7 +12824,7 @@ function detectFormat(filePath) {
12782
12824
  // src/commands/hub.ts
12783
12825
  import { existsSync as existsSync61 } from "fs";
12784
12826
  import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
12785
- import path40 from "path";
12827
+ import path41 from "path";
12786
12828
  import { spawnSync as spawnSync5 } from "child_process";
12787
12829
  import "commander";
12788
12830
  import {
@@ -12801,7 +12843,7 @@ function registerHub(program2) {
12801
12843
  hub.command("init <hubPath>").description(
12802
12844
  "Initialize a new team-knowledge hub repo at <hubPath>.\n\n Creates a git repo with a .ai/ directory structure ready for shared memories.\n\n Example:\n haive hub init ../team-hub\n haive hub init /srv/git/team-knowledge\n"
12803
12845
  ).action(async (hubPath) => {
12804
- const absPath = path40.resolve(hubPath);
12846
+ const absPath = path41.resolve(hubPath);
12805
12847
  await mkdir17(absPath, { recursive: true });
12806
12848
  const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
12807
12849
  if (gitCheck.status !== 0) {
@@ -12812,10 +12854,10 @@ function registerHub(program2) {
12812
12854
  return;
12813
12855
  }
12814
12856
  }
12815
- const sharedDir = path40.join(absPath, ".ai", "memories", "shared");
12857
+ const sharedDir = path41.join(absPath, ".ai", "memories", "shared");
12816
12858
  await mkdir17(sharedDir, { recursive: true });
12817
12859
  await writeFile29(
12818
- path40.join(absPath, ".ai", "README.md"),
12860
+ path41.join(absPath, ".ai", "README.md"),
12819
12861
  `# hAIve Team Knowledge Hub
12820
12862
 
12821
12863
  This repo is a shared knowledge hub for hAIve.
@@ -12837,7 +12879,7 @@ haive hub pull # import into a project
12837
12879
  "utf8"
12838
12880
  );
12839
12881
  await writeFile29(
12840
- path40.join(absPath, ".gitignore"),
12882
+ path41.join(absPath, ".gitignore"),
12841
12883
  ".ai/.cache/\n.ai/memories/personal/\n",
12842
12884
  "utf8"
12843
12885
  );
@@ -12852,7 +12894,7 @@ haive hub pull # import into a project
12852
12894
  `
12853
12895
  Next steps:
12854
12896
  1. Add hubPath to your project's .ai/haive.config.json:
12855
- { "hubPath": "${path40.relative(process.cwd(), absPath)}" }
12897
+ { "hubPath": "${path41.relative(process.cwd(), absPath)}" }
12856
12898
  2. Run \`haive hub push\` to publish your shared memories
12857
12899
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
12858
12900
  `
@@ -12881,14 +12923,14 @@ Next steps:
12881
12923
  process.exitCode = 1;
12882
12924
  return;
12883
12925
  }
12884
- const hubRoot = path40.resolve(root, config.hubPath);
12926
+ const hubRoot = path41.resolve(root, config.hubPath);
12885
12927
  if (!existsSync61(hubRoot)) {
12886
12928
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
12887
12929
  process.exitCode = 1;
12888
12930
  return;
12889
12931
  }
12890
- const projectName = path40.basename(root);
12891
- const destDir = path40.join(hubRoot, ".ai", "memories", "shared", projectName);
12932
+ const projectName = path41.basename(root);
12933
+ const destDir = path41.join(hubRoot, ".ai", "memories", "shared", projectName);
12892
12934
  await mkdir17(destDir, { recursive: true });
12893
12935
  const all = await loadMemoriesFromDir30(paths.memoriesDir);
12894
12936
  const shared = all.filter(
@@ -12907,7 +12949,7 @@ Next steps:
12907
12949
  for (const { memory: memory2 } of shared) {
12908
12950
  const fm = memory2.frontmatter;
12909
12951
  const fileName = `${fm.id}.md`;
12910
- const destPath = path40.join(destDir, fileName);
12952
+ const destPath = path41.join(destDir, fileName);
12911
12953
  await writeFile29(destPath, serializeMemory26(memory2), "utf8");
12912
12954
  pushed++;
12913
12955
  }
@@ -12915,7 +12957,7 @@ Next steps:
12915
12957
  console.log(ui.dim(` Location: ${destDir}`));
12916
12958
  if (opts.commit) {
12917
12959
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
12918
- spawnSync5("git", ["add", path40.join(".ai", "memories", "shared", projectName)], {
12960
+ spawnSync5("git", ["add", path41.join(".ai", "memories", "shared", projectName)], {
12919
12961
  cwd: hubRoot
12920
12962
  });
12921
12963
  const commit = spawnSync5("git", ["commit", "-m", message], {
@@ -12950,13 +12992,13 @@ Next steps:
12950
12992
  process.exitCode = 1;
12951
12993
  return;
12952
12994
  }
12953
- const hubRoot = path40.resolve(root, config.hubPath);
12954
- const hubSharedDir = path40.join(hubRoot, ".ai", "memories", "shared");
12995
+ const hubRoot = path41.resolve(root, config.hubPath);
12996
+ const hubSharedDir = path41.join(hubRoot, ".ai", "memories", "shared");
12955
12997
  if (!existsSync61(hubSharedDir)) {
12956
12998
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
12957
12999
  return;
12958
13000
  }
12959
- const projectName = path40.basename(root);
13001
+ const projectName = path41.basename(root);
12960
13002
  const { readdir: readdir7 } = await import("fs/promises");
12961
13003
  const projectDirs = (await readdir7(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
12962
13004
  if (projectDirs.length === 0) {
@@ -12966,16 +13008,16 @@ Next steps:
12966
13008
  let totalImported = 0;
12967
13009
  let totalUpdated = 0;
12968
13010
  for (const sourceName of projectDirs) {
12969
- const sourceDir = path40.join(hubSharedDir, sourceName);
12970
- const destDir = path40.join(paths.memoriesDir, "shared", sourceName);
13011
+ const sourceDir = path41.join(hubSharedDir, sourceName);
13012
+ const destDir = path41.join(paths.memoriesDir, "shared", sourceName);
12971
13013
  await mkdir17(destDir, { recursive: true });
12972
13014
  const sourceFiles = (await readdir7(sourceDir)).filter((f) => f.endsWith(".md"));
12973
13015
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
12974
13016
  const existingInDest = await loadDir(destDir);
12975
13017
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
12976
13018
  for (const file of sourceFiles) {
12977
- const srcPath = path40.join(sourceDir, file);
12978
- const destPath = path40.join(destDir, file);
13019
+ const srcPath = path41.join(sourceDir, file);
13020
+ const destPath = path41.join(destDir, file);
12979
13021
  const fileContent = await readFile19(srcPath, "utf8");
12980
13022
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
12981
13023
  if (!alreadyTagged) {
@@ -13006,14 +13048,14 @@ Next steps:
13006
13048
  console.log(
13007
13049
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
13008
13050
  );
13009
- const sharedDir = path40.join(paths.memoriesDir, "shared");
13051
+ const sharedDir = path41.join(paths.memoriesDir, "shared");
13010
13052
  if (existsSync61(sharedDir)) {
13011
13053
  const { readdir: readdir7 } = await import("fs/promises");
13012
13054
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
13013
13055
  console.log(`
13014
13056
  Imported from ${sources.length} source(s):`);
13015
13057
  for (const src of sources) {
13016
- const files = (await readdir7(path40.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
13058
+ const files = (await readdir7(path41.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
13017
13059
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
13018
13060
  }
13019
13061
  } else {
@@ -13038,7 +13080,7 @@ Next steps:
13038
13080
  import "commander";
13039
13081
  import { existsSync as existsSync63 } from "fs";
13040
13082
  import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
13041
- import path41 from "path";
13083
+ import path43 from "path";
13042
13084
  import {
13043
13085
  aggregateUsage,
13044
13086
  findProjectRoot as findProjectRoot39,
@@ -13110,7 +13152,7 @@ function registerStats(program2) {
13110
13152
  });
13111
13153
  }
13112
13154
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13113
- const outAbs = path41.isAbsolute(outRelative) ? path41.resolve(outRelative) : path41.resolve(root, outRelative);
13155
+ const outAbs = path43.isAbsolute(outRelative) ? path43.resolve(outRelative) : path43.resolve(root, outRelative);
13114
13156
  const size = await usageLogSize(paths);
13115
13157
  let events = await readUsageEvents2(paths);
13116
13158
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
@@ -13145,7 +13187,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13145
13187
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
13146
13188
  events = [];
13147
13189
  }
13148
- await mkdir18(path41.dirname(outAbs), { recursive: true });
13190
+ await mkdir18(path43.dirname(outAbs), { recursive: true });
13149
13191
  const payload = {
13150
13192
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
13151
13193
  project_root: root,
@@ -13336,7 +13378,7 @@ function summarize(name, t0, payload, notes) {
13336
13378
  // src/commands/benchmark.ts
13337
13379
  import { existsSync as existsSync64 } from "fs";
13338
13380
  import { readdir as readdir5, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
13339
- import path43 from "path";
13381
+ import path44 from "path";
13340
13382
  import "commander";
13341
13383
  import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hiveai/core";
13342
13384
  function registerBenchmark(program2) {
@@ -13351,9 +13393,9 @@ function registerBenchmark(program2) {
13351
13393
  }
13352
13394
  const markdown = renderMarkdown(root, summary, rows);
13353
13395
  if (opts.out) {
13354
- const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
13396
+ const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
13355
13397
  await writeFile31(outFile, markdown, "utf8");
13356
- ui.success(`wrote ${path43.relative(process.cwd(), outFile)}`);
13398
+ ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
13357
13399
  return;
13358
13400
  }
13359
13401
  console.log(markdown);
@@ -13377,9 +13419,9 @@ function registerBenchmark(program2) {
13377
13419
  }
13378
13420
  function resolveBenchmarkRoot(dir) {
13379
13421
  const candidate = dir ?? "benchmarks/agent-benchmark";
13380
- if (path43.isAbsolute(candidate)) return candidate;
13422
+ if (path44.isAbsolute(candidate)) return candidate;
13381
13423
  const projectRoot = findProjectRoot41(process.cwd());
13382
- return path43.join(projectRoot, candidate);
13424
+ return path44.join(projectRoot, candidate);
13383
13425
  }
13384
13426
  async function collectRows(root) {
13385
13427
  if (!existsSync64(root)) throw new Error(`Benchmark directory not found: ${root}`);
@@ -13387,8 +13429,8 @@ async function collectRows(root) {
13387
13429
  const rows = [];
13388
13430
  for (const entry of entries) {
13389
13431
  if (!entry.isDirectory()) continue;
13390
- const fixtureDir = path43.join(root, entry.name);
13391
- const reportFile = path43.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
13432
+ const fixtureDir = path44.join(root, entry.name);
13433
+ const reportFile = path44.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
13392
13434
  if (!existsSync64(reportFile)) continue;
13393
13435
  const report = await readFile20(reportFile, "utf8");
13394
13436
  rows.push(parseAgentReport(entry.name, report));
@@ -13406,7 +13448,7 @@ function parseAgentReport(fixture, report) {
13406
13448
  test_iterations: countMatches(section(report, "Test Iterations"), /Iteration\s+\d+|^- /gim),
13407
13449
  terminal_failures: countMatches(section(report, "Terminal Errors"), /fail|error|not raised|exited with code 1/gi),
13408
13450
  decision_mentions: sectionBulletCount(report, "Key Decisions"),
13409
- token_proxy: estimateTokens4(report),
13451
+ report_tokens_est: estimateTokens4(report),
13410
13452
  haive_impact: /hAIve Memory Impact[\s\S]*?\b(yes|directly|changed|shaped|confirmed)\b/i.test(report)
13411
13453
  };
13412
13454
  }
@@ -13428,7 +13470,7 @@ function summarizeGroup(rows) {
13428
13470
  test_iterations: sum("test_iterations"),
13429
13471
  terminal_failures: sum("terminal_failures"),
13430
13472
  decision_mentions: sum("decision_mentions"),
13431
- token_proxy: sum("token_proxy"),
13473
+ report_tokens_est: sum("report_tokens_est"),
13432
13474
  haive_impact_count: rows.filter((r) => r.haive_impact).length
13433
13475
  };
13434
13476
  }
@@ -13440,29 +13482,31 @@ function renderMarkdown(root, summary, rows) {
13440
13482
  "",
13441
13483
  "## Summary",
13442
13484
  "",
13443
- "| Group | Fixtures | Commands | Files read | Files modified | Test iterations | Terminal failures | Decision mentions | Token proxy | hAIve impact |",
13485
+ "| Group | Fixtures | Commands | Files read | Files modified | Test iterations | Terminal failures | Decision mentions | Report tokens (est, report only) | hAIve impact |",
13444
13486
  "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
13445
13487
  groupLine("hAIve", summary.haive),
13446
13488
  groupLine("Plain", summary.plain),
13447
13489
  "",
13448
13490
  "## Fixtures",
13449
13491
  "",
13450
- "| Fixture | Group | Commands | Files read | Files modified | Test iterations | Terminal failures | Decisions | Token proxy | hAIve impact |",
13492
+ "| Fixture | Group | Commands | Files read | Files modified | Test iterations | Terminal failures | Decisions | Report tokens (est, report only) | hAIve impact |",
13451
13493
  "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |",
13452
13494
  ...rows.map(
13453
- (row) => `| \`${row.fixture}\` | ${row.group} | ${row.commands} | ${row.files_read} | ${row.files_modified} | ${row.test_iterations} | ${row.terminal_failures} | ${row.decision_mentions} | ${row.token_proxy} | ${row.haive_impact ? "yes" : "no"} |`
13495
+ (row) => `| \`${row.fixture}\` | ${row.group} | ${row.commands} | ${row.files_read} | ${row.files_modified} | ${row.test_iterations} | ${row.terminal_failures} | ${row.decision_mentions} | ${row.report_tokens_est} | ${row.haive_impact ? "yes" : "no"} |`
13454
13496
  ),
13455
13497
  "",
13456
13498
  "## Reading",
13457
13499
  "",
13458
- "The token proxy is estimated from the agent report size, not from private model billing data.",
13500
+ "`Report tokens (est)` estimates the size of the agent's WRITTEN REPORT only \u2014 a verbosity proxy, NOT",
13501
+ "the agent's total token consumption. For real per-agent token/latency, capture your runner's telemetry",
13502
+ "(e.g. subagent token counts) separately; this report can't see model billing.",
13459
13503
  "Use this report to compare relative effort and decision quality, then pair it with final test results and a human review of the diffs.",
13460
13504
  ""
13461
13505
  ];
13462
13506
  return lines.join("\n");
13463
13507
  }
13464
13508
  function groupLine(label, group) {
13465
- return `| ${label} | ${group.fixtures} | ${group.commands} | ${group.files_read} | ${group.files_modified} | ${group.test_iterations} | ${group.terminal_failures} | ${group.decision_mentions} | ${group.token_proxy} | ${group.haive_impact_count} |`;
13509
+ return `| ${label} | ${group.fixtures} | ${group.commands} | ${group.files_read} | ${group.files_modified} | ${group.test_iterations} | ${group.terminal_failures} | ${group.decision_mentions} | ${group.report_tokens_est} | ${group.haive_impact_count} |`;
13466
13510
  }
13467
13511
  function sectionBulletCount(markdown, title) {
13468
13512
  return countMatches(section(markdown, title), /^- |^\d+\.\s/gm);
@@ -13481,7 +13525,7 @@ function escapeRegExp(value) {
13481
13525
  // src/commands/eval.ts
13482
13526
  import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
13483
13527
  import { existsSync as existsSync65 } from "fs";
13484
- import path44 from "path";
13528
+ import path45 from "path";
13485
13529
  import "commander";
13486
13530
  import {
13487
13531
  aggregateRetrieval,
@@ -13577,7 +13621,7 @@ function registerEval(program2) {
13577
13621
  });
13578
13622
  if (!opts.json) ui.success(`Recorded eval score ${report.score}/100 to history.`);
13579
13623
  }
13580
- const baselineFile = opts.baselineFile ? path44.isAbsolute(opts.baselineFile) ? opts.baselineFile : path44.join(root, opts.baselineFile) : path44.join(root, ".ai", "eval", "baseline.json");
13624
+ const baselineFile = opts.baselineFile ? path45.isAbsolute(opts.baselineFile) ? opts.baselineFile : path45.join(root, opts.baselineFile) : path45.join(root, ".ai", "eval", "baseline.json");
13581
13625
  if (opts.baseline) {
13582
13626
  const snapshot = {
13583
13627
  saved_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -13586,18 +13630,18 @@ function registerEval(program2) {
13586
13630
  report,
13587
13631
  gate_precision: gatePrecision
13588
13632
  };
13589
- await mkdir19(path44.dirname(baselineFile), { recursive: true });
13633
+ await mkdir19(path45.dirname(baselineFile), { recursive: true });
13590
13634
  await writeFile33(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
13591
- if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path44.relative(root, baselineFile)}`);
13635
+ if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path45.relative(root, baselineFile)}`);
13592
13636
  }
13593
13637
  let delta = null;
13594
13638
  let gateDelta = null;
13595
13639
  if (opts.compare || opts.regressionGate) {
13596
13640
  if (!existsSync65(baselineFile)) {
13597
13641
  if (opts.regressionGate) {
13598
- if (!opts.json) ui.info(`No baseline at ${path44.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
13642
+ if (!opts.json) ui.info(`No baseline at ${path45.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
13599
13643
  } else {
13600
- ui.error(`No baseline at ${path44.relative(root, baselineFile)}. Run \`haive eval --baseline\` first.`);
13644
+ ui.error(`No baseline at ${path45.relative(root, baselineFile)}. Run \`haive eval --baseline\` first.`);
13601
13645
  process.exitCode = 1;
13602
13646
  return;
13603
13647
  }
@@ -13636,9 +13680,9 @@ function registerEval(program2) {
13636
13680
  }
13637
13681
  const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision);
13638
13682
  if (opts.out) {
13639
- const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
13683
+ const outFile = path45.isAbsolute(opts.out) ? opts.out : path45.join(root, opts.out);
13640
13684
  await writeFile33(outFile, md, "utf8");
13641
- ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
13685
+ ui.success(`wrote ${path45.relative(process.cwd(), outFile)}`);
13642
13686
  } else {
13643
13687
  console.log(md);
13644
13688
  }
@@ -13722,12 +13766,12 @@ function countCases(spec) {
13722
13766
  }
13723
13767
  async function resolveSpec(opts, root, memoriesDir) {
13724
13768
  if (opts.spec) {
13725
- const file = path44.resolve(opts.spec);
13769
+ const file = path45.resolve(opts.spec);
13726
13770
  const raw = await readFile21(file, "utf8");
13727
13771
  const spec = JSON.parse(raw);
13728
13772
  return { spec, source: file, synthesized: 0, authored: countCases(spec) };
13729
13773
  }
13730
- const defaultSpec = path44.join(root, ".ai", "eval", "spec.json");
13774
+ const defaultSpec = path45.join(root, ".ai", "eval", "spec.json");
13731
13775
  if (existsSync65(defaultSpec)) {
13732
13776
  const raw = await readFile21(defaultSpec, "utf8");
13733
13777
  const explicit = JSON.parse(raw);
@@ -13848,7 +13892,7 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision) {
13848
13892
  // src/commands/memory-suggest.ts
13849
13893
  import { mkdir as mkdir20, writeFile as writeFile34 } from "fs/promises";
13850
13894
  import { existsSync as existsSync66 } from "fs";
13851
- import path45 from "path";
13895
+ import path46 from "path";
13852
13896
  import "commander";
13853
13897
  import {
13854
13898
  aggregateUsage as aggregateUsage2,
@@ -13949,13 +13993,13 @@ function registerMemorySuggest(memory2) {
13949
13993
  });
13950
13994
  const body = renderTemplate(s, fm.id, status);
13951
13995
  const file = memoryFilePath11(paths, fm.scope, fm.id, fm.module);
13952
- await mkdir20(path45.dirname(file), { recursive: true });
13996
+ await mkdir20(path46.dirname(file), { recursive: true });
13953
13997
  if (existsSync66(file)) {
13954
- skipped.push({ query: s.query, reason: `file already exists at ${path45.relative(root, file)}` });
13998
+ skipped.push({ query: s.query, reason: `file already exists at ${path46.relative(root, file)}` });
13955
13999
  continue;
13956
14000
  }
13957
14001
  await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
13958
- created.push({ id: fm.id, file: path45.relative(root, file), query: s.query });
14002
+ created.push({ id: fm.id, file: path46.relative(root, file), query: s.query });
13959
14003
  }
13960
14004
  if (opts.json) {
13961
14005
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -14055,7 +14099,7 @@ function truncate2(text, max) {
14055
14099
  // src/commands/memory-archive.ts
14056
14100
  import { existsSync as existsSync67 } from "fs";
14057
14101
  import { writeFile as writeFile35 } from "fs/promises";
14058
- import path46 from "path";
14102
+ import path47 from "path";
14059
14103
  import "commander";
14060
14104
  import {
14061
14105
  findProjectRoot as findProjectRoot44,
@@ -14099,7 +14143,7 @@ function registerMemoryArchive(memory2) {
14099
14143
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
14100
14144
  const retired = retirementSignal2(fm, mem.body);
14101
14145
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
14102
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync67(path46.join(paths.root, p)));
14146
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync67(path47.join(paths.root, p)));
14103
14147
  const isAnchorless = !hasAnyAnchor;
14104
14148
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
14105
14149
  const u = getUsage21(usage, fm.id);
@@ -14176,7 +14220,7 @@ function parseDays(input) {
14176
14220
  // src/commands/doctor.ts
14177
14221
  import { existsSync as existsSync68, statSync as statSync2 } from "fs";
14178
14222
  import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
14179
- import path47 from "path";
14223
+ import path48 from "path";
14180
14224
  import { execFileSync, execSync as execSync3 } from "child_process";
14181
14225
  import "commander";
14182
14226
  import {
@@ -14401,7 +14445,7 @@ function registerDoctor(program2) {
14401
14445
  }
14402
14446
  }
14403
14447
  if (config.enforcement?.requireBriefingFirst) {
14404
- const claudeSettings = path47.join(root, ".claude", "settings.local.json");
14448
+ const claudeSettings = path48.join(root, ".claude", "settings.local.json");
14405
14449
  let hasClaudeEnforcement = false;
14406
14450
  if (existsSync68(claudeSettings)) {
14407
14451
  try {
@@ -14429,7 +14473,7 @@ function registerDoctor(program2) {
14429
14473
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
14430
14474
  });
14431
14475
  }
14432
- findings.push(...await collectInstallFindings(root, "0.23.0"));
14476
+ findings.push(...await collectInstallFindings(root, "0.25.0"));
14433
14477
  findings.push(...await collectToolchainFindings(root));
14434
14478
  try {
14435
14479
  const legacyRaw = execSync3("haive-mcp --version", {
@@ -14437,7 +14481,7 @@ function registerDoctor(program2) {
14437
14481
  timeout: 3e3,
14438
14482
  stdio: ["ignore", "pipe", "ignore"]
14439
14483
  }).trim();
14440
- const cliVersion = "0.23.0";
14484
+ const cliVersion = "0.25.0";
14441
14485
  if (legacyRaw && legacyRaw !== cliVersion) {
14442
14486
  findings.push({
14443
14487
  severity: "warn",
@@ -14453,9 +14497,9 @@ npm uninstall -g @hiveai/mcp`
14453
14497
  }
14454
14498
  {
14455
14499
  const configPaths = [
14456
- path47.join(root, ".mcp.json"),
14457
- path47.join(root, ".cursor", "mcp.json"),
14458
- path47.join(root, ".vscode", "mcp.json")
14500
+ path48.join(root, ".mcp.json"),
14501
+ path48.join(root, ".cursor", "mcp.json"),
14502
+ path48.join(root, ".vscode", "mcp.json")
14459
14503
  ];
14460
14504
  const staleConfigs = [];
14461
14505
  for (const cfgPath of configPaths) {
@@ -14463,7 +14507,7 @@ npm uninstall -g @hiveai/mcp`
14463
14507
  try {
14464
14508
  const raw = await readFile23(cfgPath, "utf8");
14465
14509
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
14466
- staleConfigs.push(path47.relative(root, cfgPath));
14510
+ staleConfigs.push(path48.relative(root, cfgPath));
14467
14511
  if (opts.fix && !opts.dryRun) {
14468
14512
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
14469
14513
  await writeFile36(cfgPath, updated, "utf8");
@@ -14754,7 +14798,7 @@ which -a haive`
14754
14798
  ".vscode/mcp.json"
14755
14799
  ];
14756
14800
  for (const rel of integrationFiles) {
14757
- const file = path47.join(root, rel);
14801
+ const file = path48.join(root, rel);
14758
14802
  if (!existsSync68(file)) continue;
14759
14803
  const text = await readFile23(file, "utf8").catch(() => "");
14760
14804
  for (const bin of extractAbsoluteHaiveBins(text)) {
@@ -14781,7 +14825,7 @@ which -a haive`
14781
14825
  async function collectToolchainFindings(root) {
14782
14826
  const findings = [];
14783
14827
  const pkg = await readJson(
14784
- path47.join(root, "package.json")
14828
+ path48.join(root, "package.json")
14785
14829
  );
14786
14830
  const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
14787
14831
  if (!wantsPnpm) return findings;
@@ -14800,9 +14844,9 @@ async function collectToolchainFindings(root) {
14800
14844
  }
14801
14845
  async function collectDistFreshnessFindings(root, expectedVersion) {
14802
14846
  const findings = [];
14803
- const isHaiveWorkspace = (await readJson(path47.join(root, "package.json")))?.name === "haive-monorepo";
14847
+ const isHaiveWorkspace = (await readJson(path48.join(root, "package.json")))?.name === "haive-monorepo";
14804
14848
  if (!isHaiveWorkspace) return findings;
14805
- const cliDist = path47.join(root, "packages/cli/dist/index.js");
14849
+ const cliDist = path48.join(root, "packages/cli/dist/index.js");
14806
14850
  if (!existsSync68(cliDist)) {
14807
14851
  findings.push({
14808
14852
  severity: "warn",
@@ -14827,7 +14871,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14827
14871
  "packages/core/src/index.ts",
14828
14872
  "packages/mcp/src/server.ts",
14829
14873
  "packages/cli/src/index.ts"
14830
- ].map((rel) => path47.join(root, rel)).filter(existsSync68);
14874
+ ].map((rel) => path48.join(root, rel)).filter(existsSync68);
14831
14875
  if (sourceFiles.length > 0) {
14832
14876
  const distMtime = statSync2(cliDist).mtimeMs;
14833
14877
  const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
@@ -14845,7 +14889,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14845
14889
  }
14846
14890
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
14847
14891
  const findings = [];
14848
- const rootPkg = await readJson(path47.join(root, "package.json"));
14892
+ const rootPkg = await readJson(path48.join(root, "package.json"));
14849
14893
  const workspacePackages = [
14850
14894
  "packages/core/package.json",
14851
14895
  "packages/embeddings/package.json",
@@ -14854,7 +14898,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
14854
14898
  ];
14855
14899
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
14856
14900
  rel,
14857
- pkg: await readJson(path47.join(root, rel))
14901
+ pkg: await readJson(path48.join(root, rel))
14858
14902
  })))).filter((item) => item.pkg);
14859
14903
  const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
14860
14904
  if (!isHaiveWorkspace) return findings;
@@ -15359,21 +15403,21 @@ function registerMemorySuggestTopic(memory2) {
15359
15403
  }
15360
15404
 
15361
15405
  // src/commands/resolve-project.ts
15362
- import path48 from "path";
15406
+ import path49 from "path";
15363
15407
  import "commander";
15364
15408
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
15365
15409
  function registerResolveProject(program2) {
15366
15410
  program2.command("resolve-project").description(
15367
15411
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
15368
15412
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
15369
- const info = resolveProjectInfo2({ cwd: path48.resolve(opts.dir) });
15413
+ const info = resolveProjectInfo2({ cwd: path49.resolve(opts.dir) });
15370
15414
  console.log(JSON.stringify({ ok: true, info }, null, 2));
15371
15415
  });
15372
15416
  }
15373
15417
 
15374
15418
  // src/commands/runtime-journal.ts
15375
15419
  import { existsSync as existsSync71 } from "fs";
15376
- import path49 from "path";
15420
+ import path50 from "path";
15377
15421
  import "commander";
15378
15422
  import {
15379
15423
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
@@ -15387,15 +15431,15 @@ function registerRuntime(program2) {
15387
15431
  );
15388
15432
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
15389
15433
  journal.command("append").description("Append one JSON line to .ai/.runtime/session-journal.ndjson").argument("<message>", "short text to log").option("-k, --kind <kind>", "note | session_end | mcp", "note").option("-d, --dir <dir>", "project root", process.cwd()).action(async (message, opts) => {
15390
- const root = path49.resolve(opts.dir ?? process.cwd());
15434
+ const root = path50.resolve(opts.dir ?? process.cwd());
15391
15435
  const paths = resolveHaivePaths45(findProjectRoot49(root));
15392
15436
  const raw = opts.kind ?? "note";
15393
15437
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
15394
15438
  await appendRuntimeJournalEntry3(paths, { kind, message });
15395
- ui.success(`Appended to ${path49.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
15439
+ ui.success(`Appended to ${path50.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
15396
15440
  });
15397
15441
  journal.command("tail").description("Print the last N entries from the runtime session journal as JSON").option("-n, --limit <n>", "number of lines", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
15398
- const root = path49.resolve(opts.dir ?? process.cwd());
15442
+ const root = path50.resolve(opts.dir ?? process.cwd());
15399
15443
  const paths = resolveHaivePaths45(findProjectRoot49(root));
15400
15444
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
15401
15445
  if (!existsSync71(paths.haiveDir)) {
@@ -15414,7 +15458,7 @@ function registerRuntime(program2) {
15414
15458
 
15415
15459
  // src/commands/memory-timeline.ts
15416
15460
  import { existsSync as existsSync73 } from "fs";
15417
- import path50 from "path";
15461
+ import path51 from "path";
15418
15462
  import "commander";
15419
15463
  import {
15420
15464
  collectTimelineEntries as collectTimelineEntries2,
@@ -15430,7 +15474,7 @@ function registerMemoryTimeline(memory2) {
15430
15474
  process.exitCode = 1;
15431
15475
  return;
15432
15476
  }
15433
- const root = path50.resolve(opts.dir ?? process.cwd());
15477
+ const root = path51.resolve(opts.dir ?? process.cwd());
15434
15478
  const paths = resolveHaivePaths46(findProjectRoot50(root));
15435
15479
  if (!existsSync73(paths.memoriesDir)) {
15436
15480
  ui.error("No memories \u2014 run `haive init`.");
@@ -15451,7 +15495,7 @@ function registerMemoryTimeline(memory2) {
15451
15495
 
15452
15496
  // src/commands/memory-conflict-candidates.ts
15453
15497
  import { existsSync as existsSync74 } from "fs";
15454
- import path51 from "path";
15498
+ import path53 from "path";
15455
15499
  import "commander";
15456
15500
  import {
15457
15501
  findLexicalConflictPairs as findLexicalConflictPairs2,
@@ -15473,7 +15517,7 @@ function registerMemoryConflictCandidates(memory2) {
15473
15517
  "decision,architecture,convention,gotcha (lexical scan)",
15474
15518
  "decision,architecture"
15475
15519
  ).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) => {
15476
- const root = path51.resolve(opts.dir ?? process.cwd());
15520
+ const root = path53.resolve(opts.dir ?? process.cwd());
15477
15521
  const paths = resolveHaivePaths47(findProjectRoot51(root));
15478
15522
  if (!existsSync74(paths.memoriesDir)) {
15479
15523
  ui.error("No memories \u2014 run `haive init`.");
@@ -15513,7 +15557,7 @@ function registerMemoryConflictCandidates(memory2) {
15513
15557
  import { execFile as execFile3, execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
15514
15558
  import { existsSync as existsSync75, statSync as statSync3 } from "fs";
15515
15559
  import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir6, rm as rm3, writeFile as writeFile37 } from "fs/promises";
15516
- import path53 from "path";
15560
+ import path54 from "path";
15517
15561
  import { promisify as promisify3 } from "util";
15518
15562
  import "commander";
15519
15563
  import {
@@ -15573,7 +15617,7 @@ function registerEnforce(program2) {
15573
15617
  if (opts.claude !== false) {
15574
15618
  try {
15575
15619
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
15576
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path53.relative(root, result.settingsPath)})`);
15620
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path54.relative(root, result.settingsPath)})`);
15577
15621
  } catch (err) {
15578
15622
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
15579
15623
  }
@@ -15594,19 +15638,19 @@ function registerEnforce(program2) {
15594
15638
  enforce.command("cleanup").description("Remove generated hAIve runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
15595
15639
  const root = findProjectRoot52(opts.dir);
15596
15640
  const paths = resolveHaivePaths48(root);
15597
- const cacheDir = path53.join(paths.haiveDir, ".cache");
15641
+ const cacheDir = path54.join(paths.haiveDir, ".cache");
15598
15642
  if (existsSync75(cacheDir)) {
15599
- if (opts.dryRun) ui.info(`would clean ${path53.relative(root, cacheDir)} (preserving .gitignore)`);
15643
+ if (opts.dryRun) ui.info(`would clean ${path54.relative(root, cacheDir)} (preserving .gitignore)`);
15600
15644
  else {
15601
15645
  const removed = await cleanupCacheDir(cacheDir);
15602
- ui.success(`cleaned ${path53.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15646
+ ui.success(`cleaned ${path54.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15603
15647
  }
15604
15648
  }
15605
15649
  if (existsSync75(paths.runtimeDir)) {
15606
- if (opts.dryRun) ui.info(`would clean ${path53.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
15650
+ if (opts.dryRun) ui.info(`would clean ${path54.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
15607
15651
  else {
15608
15652
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
15609
- ui.success(`cleaned ${path53.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15653
+ ui.success(`cleaned ${path54.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15610
15654
  }
15611
15655
  }
15612
15656
  });
@@ -15959,7 +16003,7 @@ async function buildFinishReport(dir) {
15959
16003
  async function checkFailureCapture(paths, config) {
15960
16004
  const gate = config.enforcement?.failureCaptureGate ?? "warn";
15961
16005
  if (gate === "off") return [];
15962
- const obsFile = path53.join(paths.haiveDir, ".cache", "observations.jsonl");
16006
+ const obsFile = path54.join(paths.haiveDir, ".cache", "observations.jsonl");
15963
16007
  if (!existsSync75(obsFile)) return [];
15964
16008
  const failures = [];
15965
16009
  try {
@@ -16031,7 +16075,7 @@ async function runWithEnforcement(command, args, opts) {
16031
16075
  process.exit(2);
16032
16076
  }
16033
16077
  ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
16034
- ui.info(`Briefing written to ${path53.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
16078
+ ui.info(`Briefing written to ${path54.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
16035
16079
  const child = spawn6(command, args, {
16036
16080
  cwd: root,
16037
16081
  stdio: "inherit",
@@ -16081,9 +16125,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
16081
16125
  source: "haive-run",
16082
16126
  memoryIds: briefing.memories.map((m) => m.id)
16083
16127
  });
16084
- const dir = path53.join(paths.runtimeDir, "enforcement", "briefings");
16128
+ const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
16085
16129
  await mkdir21(dir, { recursive: true });
16086
- const file = path53.join(dir, `${sessionId}.md`);
16130
+ const file = path54.join(dir, `${sessionId}.md`);
16087
16131
  const parts = [
16088
16132
  "# hAIve Briefing",
16089
16133
  "",
@@ -16141,7 +16185,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
16141
16185
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
16142
16186
  });
16143
16187
  }
16144
- findings.push(...await inspectIntegrationVersions(root, "0.23.0"));
16188
+ findings.push(...await inspectIntegrationVersions(root, "0.25.0"));
16145
16189
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
16146
16190
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
16147
16191
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -16292,7 +16336,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
16292
16336
  const consulted = new Set(marker?.memory_ids ?? []);
16293
16337
  const missing = relevant.filter(({ memory: memory2, filePath }) => {
16294
16338
  if (consulted.has(memory2.frontmatter.id)) return false;
16295
- if (changedSet.has(path53.relative(paths.root, filePath))) return false;
16339
+ if (changedSet.has(path54.relative(paths.root, filePath))) return false;
16296
16340
  return true;
16297
16341
  }).map(({ memory: memory2 }) => memory2);
16298
16342
  if (missing.length === 0) {
@@ -16500,16 +16544,16 @@ async function cleanupRuntimeDir(runtimeDir) {
16500
16544
  for (const entry of entries) {
16501
16545
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
16502
16546
  if (entry.name === "enforcement") {
16503
- removed += await cleanupEnforcementDir(path53.join(runtimeDir, entry.name));
16547
+ removed += await cleanupEnforcementDir(path54.join(runtimeDir, entry.name));
16504
16548
  continue;
16505
16549
  }
16506
- await rm3(path53.join(runtimeDir, entry.name), { recursive: true, force: true });
16550
+ await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
16507
16551
  removed++;
16508
16552
  }
16509
- await writeFile37(path53.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16510
- if (!existsSync75(path53.join(runtimeDir, "README.md"))) {
16553
+ await writeFile37(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16554
+ if (!existsSync75(path54.join(runtimeDir, "README.md"))) {
16511
16555
  await writeFile37(
16512
- path53.join(runtimeDir, "README.md"),
16556
+ path54.join(runtimeDir, "README.md"),
16513
16557
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
16514
16558
  "utf8"
16515
16559
  );
@@ -16522,10 +16566,10 @@ async function cleanupCacheDir(cacheDir) {
16522
16566
  const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
16523
16567
  for (const entry of entries) {
16524
16568
  if (entry.name === ".gitignore") continue;
16525
- await rm3(path53.join(cacheDir, entry.name), { recursive: true, force: true });
16569
+ await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
16526
16570
  removed++;
16527
16571
  }
16528
- await writeFile37(path53.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16572
+ await writeFile37(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16529
16573
  return removed;
16530
16574
  }
16531
16575
  async function cleanupEnforcementDir(enforcementDir) {
@@ -16533,7 +16577,7 @@ async function cleanupEnforcementDir(enforcementDir) {
16533
16577
  const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
16534
16578
  for (const entry of entries) {
16535
16579
  if (entry.name === "briefings") continue;
16536
- await rm3(path53.join(enforcementDir, entry.name), { recursive: true, force: true });
16580
+ await rm3(path54.join(enforcementDir, entry.name), { recursive: true, force: true });
16537
16581
  removed++;
16538
16582
  }
16539
16583
  return removed;
@@ -16549,7 +16593,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
16549
16593
  ];
16550
16594
  const findings = [];
16551
16595
  for (const rel of files) {
16552
- const file = path53.join(root, rel);
16596
+ const file = path54.join(root, rel);
16553
16597
  if (!existsSync75(file)) continue;
16554
16598
  const text = await readFile24(file, "utf8").catch(() => "");
16555
16599
  for (const bin of extractAbsoluteHaiveBins2(text)) {
@@ -16765,7 +16809,7 @@ function isShippablePath(file) {
16765
16809
  }
16766
16810
  var CI_SKIP_DIRECTIVE = /\[skip ci\]|\[ci skip\]|\[no ci\]|\[skip actions\]|\*\*\*NO_CI\*\*\*|skip-checks: *true/i;
16767
16811
  async function checkCommitMessageSkipCi(root, msgfile) {
16768
- const file = path53.isAbsolute(msgfile) ? msgfile : path53.join(root, msgfile);
16812
+ const file = path54.isAbsolute(msgfile) ? msgfile : path54.join(root, msgfile);
16769
16813
  const raw = await readFile24(file, "utf8").catch(() => "");
16770
16814
  const cleaned = raw.split("\n").filter((line) => !line.startsWith("#")).join("\n");
16771
16815
  if (!CI_SKIP_DIRECTIVE.test(cleaned)) return { block: false, message: "" };
@@ -16794,7 +16838,7 @@ async function inspectReleaseVersionState(root, upstream) {
16794
16838
  }
16795
16839
  async function readPackageVersion(root, relPath) {
16796
16840
  try {
16797
- const data = JSON.parse(await readFile24(path53.join(root, relPath), "utf8"));
16841
+ const data = JSON.parse(await readFile24(path54.join(root, relPath), "utf8"));
16798
16842
  return typeof data.version === "string" ? data.version : void 0;
16799
16843
  } catch {
16800
16844
  return void 0;
@@ -16982,8 +17026,8 @@ function buildScore(findings, threshold = 80) {
16982
17026
  };
16983
17027
  }
16984
17028
  async function installGitEnforcement(root) {
16985
- const hooksDir = path53.join(root, ".git", "hooks");
16986
- if (!existsSync75(path53.join(root, ".git"))) {
17029
+ const hooksDir = path54.join(root, ".git", "hooks");
17030
+ if (!existsSync75(path54.join(root, ".git"))) {
16987
17031
  ui.warn("No .git directory found; git enforcement hooks skipped.");
16988
17032
  return;
16989
17033
  }
@@ -17012,7 +17056,7 @@ haive enforce commit-msg "$1" --dir . || exit $?
17012
17056
  }
17013
17057
  ];
17014
17058
  for (const hook of hooks) {
17015
- const file = path53.join(hooksDir, hook.name);
17059
+ const file = path54.join(hooksDir, hook.name);
17016
17060
  if (existsSync75(file)) {
17017
17061
  const current = await readFile24(file, "utf8").catch(() => "");
17018
17062
  if (current.includes(ENFORCE_HOOK_MARKER)) {
@@ -17030,8 +17074,8 @@ ${hook.body}`, "utf8");
17030
17074
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push, commit-msg");
17031
17075
  }
17032
17076
  async function installCiEnforcement(root) {
17033
- const workflowPath = path53.join(root, ".github", "workflows", "haive-enforcement.yml");
17034
- await mkdir21(path53.dirname(workflowPath), { recursive: true });
17077
+ const workflowPath = path54.join(root, ".github", "workflows", "haive-enforcement.yml");
17078
+ await mkdir21(path54.dirname(workflowPath), { recursive: true });
17035
17079
  if (existsSync75(workflowPath)) {
17036
17080
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
17037
17081
  return;
@@ -17063,7 +17107,7 @@ jobs:
17063
17107
  HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
17064
17108
  run: haive enforce ci
17065
17109
  `, "utf8");
17066
- ui.success(`Created ${path53.relative(root, workflowPath)}`);
17110
+ ui.success(`Created ${path54.relative(root, workflowPath)}`);
17067
17111
  }
17068
17112
  function printReport(report, json, explain = false) {
17069
17113
  if (json) {
@@ -17167,8 +17211,8 @@ function extractToolPaths(payload, root) {
17167
17211
  }
17168
17212
  function normalizeToolPath(file, root) {
17169
17213
  const normalized = file.replace(/\\/g, "/");
17170
- if (!path53.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
17171
- return path53.relative(root, normalized).replace(/\\/g, "/");
17214
+ if (!path54.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
17215
+ return path54.relative(root, normalized).replace(/\\/g, "/");
17172
17216
  }
17173
17217
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
17174
17218
  if (!existsSync75(paths.memoriesDir)) return [];
@@ -17216,7 +17260,7 @@ async function readStdin2(maxBytes) {
17216
17260
  }
17217
17261
  var ATOMIC_STAGE_EXCLUDE = ["/.usage/", "/.runtime/", "/.cache/"];
17218
17262
  async function stageResyncedArtifacts(root, paths) {
17219
- const aiRel = path53.relative(root, paths.haiveDir);
17263
+ const aiRel = path54.relative(root, paths.haiveDir);
17220
17264
  const out = await runCommand4("git", ["diff", "--name-only", "--", aiRel], root).catch(() => "");
17221
17265
  const toStage = out.split("\n").map((line) => line.trim()).filter(Boolean).filter((file) => !ATOMIC_STAGE_EXCLUDE.some((excl) => `/${file}`.includes(excl)));
17222
17266
  if (toStage.length === 0) return;
@@ -17258,7 +17302,7 @@ function registerRun(program2) {
17258
17302
  import { execFile as execFile4 } from "child_process";
17259
17303
  import { existsSync as existsSync76 } from "fs";
17260
17304
  import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
17261
- import path54 from "path";
17305
+ import path55 from "path";
17262
17306
  import { promisify as promisify4 } from "util";
17263
17307
  import "commander";
17264
17308
  import {
@@ -17303,7 +17347,7 @@ function registerSensors(program2) {
17303
17347
  const root = findProjectRoot53(opts.dir);
17304
17348
  const paths = resolveHaivePaths49(root);
17305
17349
  const memories = await runnableSensorMemories(paths);
17306
- const diff = opts.diffFile ? await readFile25(path54.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
17350
+ const diff = opts.diffFile ? await readFile25(path55.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
17307
17351
  const targets = sensorTargetsFromDiff3(diff);
17308
17352
  const hits = runSensors3(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
17309
17353
  const config = await loadConfig15(paths);
@@ -17415,13 +17459,13 @@ function registerSensors(program2) {
17415
17459
  const root = findProjectRoot53(opts.dir);
17416
17460
  const paths = resolveHaivePaths49(root);
17417
17461
  const rows = await sensorRows(paths);
17418
- const outDir = path54.resolve(root, opts.outDir ?? ".ai/generated");
17462
+ const outDir = path55.resolve(root, opts.outDir ?? ".ai/generated");
17419
17463
  await mkdir23(outDir, { recursive: true });
17420
- const outPath = path54.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
17464
+ const outPath = path55.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
17421
17465
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
17422
17466
  await writeFile38(outPath, content, "utf8");
17423
17467
  if (format === "grep") await chmod3(outPath, 493);
17424
- ui.success(`Exported ${rows.length} sensor(s): ${path54.relative(root, outPath)}`);
17468
+ ui.success(`Exported ${rows.length} sensor(s): ${path55.relative(root, outPath)}`);
17425
17469
  });
17426
17470
  }
17427
17471
  async function sensorRows(paths) {
@@ -17494,7 +17538,7 @@ function shellQuote(value) {
17494
17538
  // src/commands/ingest.ts
17495
17539
  import { existsSync as existsSync77 } from "fs";
17496
17540
  import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
17497
- import path55 from "path";
17541
+ import path56 from "path";
17498
17542
  import "commander";
17499
17543
  import {
17500
17544
  draftsFromFindings as draftsFromFindings2,
@@ -17551,7 +17595,7 @@ function registerIngest(program2) {
17551
17595
  process.exitCode = 1;
17552
17596
  return;
17553
17597
  }
17554
- const reportPath = path55.resolve(root, file);
17598
+ const reportPath = path56.resolve(root, file);
17555
17599
  if (!existsSync77(reportPath)) {
17556
17600
  ui.error(`Report file not found: ${reportPath}`);
17557
17601
  process.exitCode = 1;
@@ -17640,13 +17684,13 @@ function registerIngest(program2) {
17640
17684
  await writeDraft2(paths, draft);
17641
17685
  created++;
17642
17686
  }
17643
- ui.success(`Created ${created} proposed memory(ies) under ${path55.relative(root, paths.memoriesDir)}/`);
17687
+ ui.success(`Created ${created} proposed memory(ies) under ${path56.relative(root, paths.memoriesDir)}/`);
17644
17688
  ui.info("Review with `haive memory pending`; promote sensors with `haive sensors promote <id> --yes`.");
17645
17689
  });
17646
17690
  }
17647
17691
  async function writeDraft2(paths, draft) {
17648
17692
  const file = memoryFilePath12(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
17649
- await mkdir24(path55.dirname(file), { recursive: true });
17693
+ await mkdir24(path56.dirname(file), { recursive: true });
17650
17694
  await writeFile39(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
17651
17695
  return file;
17652
17696
  }
@@ -17738,6 +17782,18 @@ function renderDashboard(r) {
17738
17782
  console.log(` ${ui.dim("scopes:")} ${formatCounts(inv.by_scope)}`);
17739
17783
  console.log(` ${ui.dim("types: ")} ${formatCounts(inv.by_type)}`);
17740
17784
  console.log();
17785
+ console.log(ui.bold("Value") + ui.dim(" (what hAIve demonstrably earned \u2014 vs its per-task cost)"));
17786
+ const blocked = prevention.trend.last_30d;
17787
+ const demonstrated = impact.high;
17788
+ console.log(
17789
+ ` ${blocked > 0 ? ui.green(`${blocked} repeat${blocked === 1 ? "" : "s"} blocked (30d)`) : "0 repeats blocked (30d)"} \xB7 ${demonstrated} high-impact memor${demonstrated === 1 ? "y" : "ies"} (applied/prevented) \xB7 ${inv.active} active polic${inv.active === 1 ? "y" : "ies"} surfaceable`
17790
+ );
17791
+ console.log(
17792
+ ui.dim(
17793
+ " Cost is real: the briefing adds context to every task; the payoff is downstream (defects/incidents avoided), not the agent's token bill."
17794
+ )
17795
+ );
17796
+ console.log();
17741
17797
  console.log(ui.bold("Prevention") + ui.dim(" (caught-for-you outcome)"));
17742
17798
  console.log(
17743
17799
  ` ${prevention.trend.last_30d} catch${prevention.trend.last_30d === 1 ? "" : "es"} in 30d \xB7 ${prevention.recurrence.recurring_count} recurrence${prevention.recurrence.recurring_count === 1 ? "" : "s"} to review \xB7 ${prevention.trend.last_7d} in 7d`
@@ -17825,7 +17881,7 @@ function warnNum(n) {
17825
17881
  import { execFile as execFile5 } from "child_process";
17826
17882
  import { cp, readFile as readFile27 } from "fs/promises";
17827
17883
  import { existsSync as existsSync79 } from "fs";
17828
- import path56 from "path";
17884
+ import path57 from "path";
17829
17885
  import { promisify as promisify5 } from "util";
17830
17886
  import "commander";
17831
17887
  import { findProjectRoot as findProjectRoot56 } from "@hiveai/core";
@@ -17834,7 +17890,7 @@ function registerDevLink(program2) {
17834
17890
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on hAIve itself.");
17835
17891
  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) => {
17836
17892
  const root = findProjectRoot56(opts.dir);
17837
- if (!existsSync79(path56.join(root, "packages", "cli", "dist", "index.js"))) {
17893
+ if (!existsSync79(path57.join(root, "packages", "cli", "dist", "index.js"))) {
17838
17894
  ui.error(`Not the hAIve monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
17839
17895
  process.exitCode = 1;
17840
17896
  return;
@@ -17843,9 +17899,9 @@ function registerDevLink(program2) {
17843
17899
  try {
17844
17900
  globalModules = (await exec3("npm", ["root", "-g"])).stdout.trim();
17845
17901
  } catch {
17846
- globalModules = path56.join(path56.dirname(path56.dirname(process.execPath)), "lib", "node_modules");
17902
+ globalModules = path57.join(path57.dirname(path57.dirname(process.execPath)), "lib", "node_modules");
17847
17903
  }
17848
- const globalHive = path56.join(globalModules, "@hiveai");
17904
+ const globalHive = path57.join(globalModules, "@hiveai");
17849
17905
  if (!existsSync79(globalHive)) {
17850
17906
  ui.error(`No global @hiveai install at ${globalHive}. Install once with \`npm i -g @hiveai/cli\`, then re-run.`);
17851
17907
  process.exitCode = 1;
@@ -17853,21 +17909,21 @@ function registerDevLink(program2) {
17853
17909
  }
17854
17910
  const linked = [];
17855
17911
  const copyDist = async (fromPkg, toDistDir) => {
17856
- const from = path56.join(root, "packages", fromPkg, "dist");
17857
- if (!existsSync79(from) || !existsSync79(path56.dirname(toDistDir))) return;
17912
+ const from = path57.join(root, "packages", fromPkg, "dist");
17913
+ if (!existsSync79(from) || !existsSync79(path57.dirname(toDistDir))) return;
17858
17914
  await cp(from, toDistDir, { recursive: true });
17859
- linked.push(path56.relative(globalModules, toDistDir));
17915
+ linked.push(path57.relative(globalModules, toDistDir));
17860
17916
  };
17861
17917
  for (const pkg of ["cli", "mcp"]) {
17862
- await copyDist(pkg, path56.join(globalHive, pkg, "dist"));
17918
+ await copyDist(pkg, path57.join(globalHive, pkg, "dist"));
17863
17919
  for (const nested of ["core", "embeddings"]) {
17864
- await copyDist(nested, path56.join(globalHive, pkg, "node_modules", "@hiveai", nested, "dist"));
17920
+ await copyDist(nested, path57.join(globalHive, pkg, "node_modules", "@hiveai", nested, "dist"));
17865
17921
  }
17866
17922
  }
17867
- await copyDist("core", path56.join(globalHive, "core", "dist"));
17923
+ await copyDist("core", path57.join(globalHive, "core", "dist"));
17868
17924
  let version = "unknown";
17869
17925
  try {
17870
- version = JSON.parse(await readFile27(path56.join(root, "package.json"), "utf8")).version ?? "unknown";
17926
+ version = JSON.parse(await readFile27(path57.join(root, "package.json"), "utf8")).version ?? "unknown";
17871
17927
  } catch {
17872
17928
  }
17873
17929
  if (opts.json) {
@@ -17887,7 +17943,7 @@ function registerDevLink(program2) {
17887
17943
  // src/commands/coverage.ts
17888
17944
  import { readFile as readFile28 } from "fs/promises";
17889
17945
  import { existsSync as existsSync80 } from "fs";
17890
- import path57 from "path";
17946
+ import path58 from "path";
17891
17947
  import "commander";
17892
17948
  import {
17893
17949
  findCoverageGaps,
@@ -17911,7 +17967,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
17911
17967
  }
17912
17968
  for (const f of obs.files ?? []) {
17913
17969
  if (typeof f !== "string" || !f) continue;
17914
- const rel = path57.isAbsolute(f) ? path57.relative(root, f) : f;
17970
+ const rel = path58.isAbsolute(f) ? path58.relative(root, f) : f;
17915
17971
  if (rel.startsWith("..")) continue;
17916
17972
  files.push(rel);
17917
17973
  }
@@ -17955,7 +18011,7 @@ function registerCoverage(program2) {
17955
18011
  }) : null;
17956
18012
  const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
17957
18013
  const sinceMs = Date.now() - days * 864e5;
17958
- const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path57.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
18014
+ const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path58.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
17959
18015
  const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
17960
18016
  const memories = await loadMemoriesFromDir27(paths.memoriesDir);
17961
18017
  const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
@@ -17994,7 +18050,7 @@ function registerCoverage(program2) {
17994
18050
  // src/commands/merge-driver.ts
17995
18051
  import { execFileSync as execFileSync3 } from "child_process";
17996
18052
  import { readFileSync, writeFileSync, existsSync as existsSync81 } from "fs";
17997
- import path58 from "path";
18053
+ import path59 from "path";
17998
18054
  import "commander";
17999
18055
  import { findProjectRoot as findProjectRoot58, mergeMemoryVersions } from "@hiveai/core";
18000
18056
  var GITATTRIBUTES_MARK = "# hAIve merge driver";
@@ -18026,7 +18082,7 @@ function registerMergeDriver(program2) {
18026
18082
  process.exitCode = 1;
18027
18083
  return;
18028
18084
  }
18029
- const gaPath = path58.join(root, ".gitattributes");
18085
+ const gaPath = path59.join(root, ".gitattributes");
18030
18086
  let content = existsSync81(gaPath) ? readFileSync(gaPath, "utf8") : "";
18031
18087
  if (!content.includes(GITATTRIBUTES_MARK)) {
18032
18088
  if (content.length > 0 && !content.endsWith("\n")) content += "\n";
@@ -18113,7 +18169,7 @@ function registerMemoryResolveConflict(memory2) {
18113
18169
  import { execFile as execFile6 } from "child_process";
18114
18170
  import { mkdir as mkdir25, writeFile as writeFile41 } from "fs/promises";
18115
18171
  import { existsSync as existsSync84 } from "fs";
18116
- import path59 from "path";
18172
+ import path60 from "path";
18117
18173
  import { promisify as promisify6 } from "util";
18118
18174
  import "commander";
18119
18175
  import {
@@ -18175,7 +18231,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
18175
18231
  `;
18176
18232
  const file = memoryFilePath13(paths, fm.scope, fm.id, fm.module);
18177
18233
  if (existsSync84(file)) continue;
18178
- await mkdir25(path59.dirname(file), { recursive: true });
18234
+ await mkdir25(path60.dirname(file), { recursive: true });
18179
18235
  await writeFile41(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
18180
18236
  written += 1;
18181
18237
  }
@@ -18208,7 +18264,7 @@ async function readCommits(root, days) {
18208
18264
 
18209
18265
  // src/commands/bridges.ts
18210
18266
  import { existsSync as existsSync85 } from "fs";
18211
- import path60 from "path";
18267
+ import path61 from "path";
18212
18268
  import "commander";
18213
18269
  import {
18214
18270
  findProjectRoot as findProjectRoot61,
@@ -18248,7 +18304,7 @@ function registerBridges(program2) {
18248
18304
  targets = BRIDGE_TARGETS3;
18249
18305
  } else {
18250
18306
  targets = BRIDGE_TARGETS3.filter(
18251
- (t) => existsSync85(path60.join(root, BRIDGE_TARGET_PATH3[t]))
18307
+ (t) => existsSync85(path61.join(root, BRIDGE_TARGET_PATH3[t]))
18252
18308
  );
18253
18309
  if (targets.length === 0) {
18254
18310
  ui.info(
@@ -18277,7 +18333,7 @@ function registerBridges(program2) {
18277
18333
  console.log(ui.bold("hAIve bridge targets:"));
18278
18334
  for (const target of BRIDGE_TARGETS3) {
18279
18335
  const relPath = BRIDGE_TARGET_PATH3[target];
18280
- const exists = existsSync85(path60.join(root, relPath));
18336
+ const exists = existsSync85(path61.join(root, relPath));
18281
18337
  const marker = exists ? ui.dim("\u2713") : ui.dim("\xB7");
18282
18338
  console.log(` ${marker} ${target.padEnd(10)} ${relPath}${exists ? "" : " (not present)"}`);
18283
18339
  }
@@ -18288,7 +18344,7 @@ function registerBridges(program2) {
18288
18344
 
18289
18345
  // src/index.ts
18290
18346
  var program = new Command64();
18291
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.23.0").option("--advanced", "show maintenance and experimental commands in help");
18347
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.25.0").option("--advanced", "show maintenance and experimental commands in help");
18292
18348
  registerInit(program);
18293
18349
  registerWelcome(program);
18294
18350
  registerResolveProject(program);