@hiveai/cli 0.23.0 → 0.24.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,7 +2249,7 @@ 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,
@@ -3374,7 +3378,7 @@ ${mem.body}`;
3374
3378
  const content = serializeMemory2({ frontmatter: fm, body: `${titledBody}
3375
3379
 
3376
3380
  ${SEED_FOOTER(stack)}` });
3377
- await mkdir5(path10.dirname(filePath), { recursive: true });
3381
+ await mkdir5(path11.dirname(filePath), { recursive: true });
3378
3382
  await writeFile6(filePath, content, "utf8");
3379
3383
  existingTopics.add(topic);
3380
3384
  existingSignatures.add(signature);
@@ -3386,7 +3390,7 @@ ${SEED_FOOTER(stack)}` });
3386
3390
 
3387
3391
  // src/commands/init.ts
3388
3392
  var execFileAsync = promisify2(execFile2);
3389
- var HAIVE_GITHUB_ACTION_REF = `v${"0.23.0"}`;
3393
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.24.0"}`;
3390
3394
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3391
3395
 
3392
3396
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3614,7 +3618,7 @@ function registerInit(program2) {
3614
3618
  "approve user-level AI client configuration prompts during agent setup",
3615
3619
  false
3616
3620
  ).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);
3621
+ const root = path12.resolve(opts.dir);
3618
3622
  const paths = resolveHaivePaths6(root);
3619
3623
  const autopilot = opts.manual !== true;
3620
3624
  const json = opts.json === true;
@@ -3641,7 +3645,7 @@ function registerInit(program2) {
3641
3645
  await mkdir6(paths.moduleDir, { recursive: true });
3642
3646
  await mkdir6(paths.modulesContextDir, { recursive: true });
3643
3647
  await ensureAiRuntimeLayout(paths.runtimeDir);
3644
- await ensureAiCacheLayout(path11.join(paths.haiveDir, ".cache"));
3648
+ await ensureAiCacheLayout(path12.join(paths.haiveDir, ".cache"));
3645
3649
  if (!existsSync11(paths.projectContext)) {
3646
3650
  if (wantBootstrap) {
3647
3651
  ui.info("Bootstrapping project context from local files\u2026");
@@ -3655,11 +3659,11 @@ function registerInit(program2) {
3655
3659
  }
3656
3660
  } else {
3657
3661
  await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
3658
- ui.success(`Created ${path11.relative(root, paths.projectContext)}`);
3662
+ ui.success(`Created ${path12.relative(root, paths.projectContext)}`);
3659
3663
  }
3660
3664
  }
3661
3665
  const configExists = existsSync11(
3662
- path11.join(paths.haiveDir, "haive.config.json")
3666
+ path12.join(paths.haiveDir, "haive.config.json")
3663
3667
  );
3664
3668
  if (!configExists) {
3665
3669
  await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
@@ -3726,13 +3730,13 @@ function registerInit(program2) {
3726
3730
  }
3727
3731
  const wantCi = opts.withCi || autopilot;
3728
3732
  if (wantCi) {
3729
- const ciPath = path11.join(root, ".github", "workflows", "haive-sync.yml");
3733
+ const ciPath = path12.join(root, ".github", "workflows", "haive-sync.yml");
3730
3734
  if (existsSync11(ciPath)) {
3731
3735
  ui.info("CI workflow already exists \u2014 skipped");
3732
3736
  } else {
3733
- await mkdir6(path11.dirname(ciPath), { recursive: true });
3737
+ await mkdir6(path12.dirname(ciPath), { recursive: true });
3734
3738
  await writeFile7(ciPath, CI_WORKFLOW, "utf8");
3735
- ui.success(`Created ${path11.relative(root, ciPath)}`);
3739
+ ui.success(`Created ${path12.relative(root, ciPath)}`);
3736
3740
  }
3737
3741
  }
3738
3742
  if (autopilot) {
@@ -3772,7 +3776,7 @@ function registerInit(program2) {
3772
3776
  interactive: process.stdin.isTTY
3773
3777
  });
3774
3778
  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)})`);
3779
+ if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path12.relative(root, r.path)})`);
3776
3780
  else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
3777
3781
  }
3778
3782
  for (const r of agentSetup.global_results) {
@@ -3871,7 +3875,7 @@ async function autoDetectStacksFromRoot(root) {
3871
3875
  let requirementsTxt;
3872
3876
  let goMod;
3873
3877
  let pomXml;
3874
- const pkgPath = path11.join(root, "package.json");
3878
+ const pkgPath = path12.join(root, "package.json");
3875
3879
  if (existsSync11(pkgPath)) {
3876
3880
  try {
3877
3881
  const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
@@ -3880,7 +3884,7 @@ async function autoDetectStacksFromRoot(root) {
3880
3884
  }
3881
3885
  }
3882
3886
  for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
3883
- const reqPath = path11.join(root, name);
3887
+ const reqPath = path12.join(root, name);
3884
3888
  if (existsSync11(reqPath)) {
3885
3889
  try {
3886
3890
  requirementsTxt = await readFile6(reqPath, "utf8");
@@ -3889,14 +3893,14 @@ async function autoDetectStacksFromRoot(root) {
3889
3893
  }
3890
3894
  }
3891
3895
  }
3892
- const goModPath = path11.join(root, "go.mod");
3896
+ const goModPath = path12.join(root, "go.mod");
3893
3897
  if (existsSync11(goModPath)) {
3894
3898
  try {
3895
3899
  goMod = await readFile6(goModPath, "utf8");
3896
3900
  } catch {
3897
3901
  }
3898
3902
  }
3899
- const pomPath = path11.join(root, "pom.xml");
3903
+ const pomPath = path12.join(root, "pom.xml");
3900
3904
  if (existsSync11(pomPath)) {
3901
3905
  try {
3902
3906
  pomXml = await readFile6(pomPath, "utf8");
@@ -3904,7 +3908,7 @@ async function autoDetectStacksFromRoot(root) {
3904
3908
  }
3905
3909
  }
3906
3910
  let composerJson;
3907
- const composerPath = path11.join(root, "composer.json");
3911
+ const composerPath = path12.join(root, "composer.json");
3908
3912
  if (existsSync11(composerPath)) {
3909
3913
  try {
3910
3914
  composerJson = await readFile6(composerPath, "utf8");
@@ -3912,16 +3916,16 @@ async function autoDetectStacksFromRoot(root) {
3912
3916
  }
3913
3917
  }
3914
3918
  let gemfile;
3915
- const gemfilePath = path11.join(root, "Gemfile");
3919
+ const gemfilePath = path12.join(root, "Gemfile");
3916
3920
  if (existsSync11(gemfilePath)) {
3917
3921
  try {
3918
3922
  gemfile = await readFile6(gemfilePath, "utf8");
3919
3923
  } catch {
3920
3924
  }
3921
3925
  }
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"));
3926
+ const hasDockerfile = existsSync11(path12.join(root, "Dockerfile"));
3927
+ const hasTurboJson = existsSync11(path12.join(root, "turbo.json"));
3928
+ const hasNxJson = existsSync11(path12.join(root, "nx.json"));
3925
3929
  let hasCsproj = false;
3926
3930
  try {
3927
3931
  const entries = await readdir2(root);
@@ -3974,7 +3978,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
3974
3978
  `;
3975
3979
  const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
3976
3980
  if (existsSync11(file)) continue;
3977
- await mkdir6(path11.dirname(file), { recursive: true });
3981
+ await mkdir6(path12.dirname(file), { recursive: true });
3978
3982
  await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
3979
3983
  written++;
3980
3984
  }
@@ -4036,12 +4040,12 @@ function printInitReport(r) {
4036
4040
  }
4037
4041
  async function writeCursorHaiveRule(root) {
4038
4042
  const relPath = ".cursor/rules/haive-mcp-required.mdc";
4039
- const target = path11.join(root, relPath);
4043
+ const target = path12.join(root, relPath);
4040
4044
  if (existsSync11(target)) {
4041
4045
  ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
4042
4046
  return;
4043
4047
  }
4044
- await mkdir6(path11.dirname(target), { recursive: true });
4048
+ await mkdir6(path12.dirname(target), { recursive: true });
4045
4049
  await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
4046
4050
  ui.success(`Created Cursor rule ${relPath}`);
4047
4051
  }
@@ -4070,25 +4074,25 @@ var RUNTIME_GITIGNORE_BODY = `*
4070
4074
  `;
4071
4075
  async function ensureAiRuntimeLayout(runtimeDir) {
4072
4076
  await mkdir6(runtimeDir, { recursive: true });
4073
- const gi = path11.join(runtimeDir, ".gitignore");
4077
+ const gi = path12.join(runtimeDir, ".gitignore");
4074
4078
  if (!existsSync11(gi)) {
4075
4079
  await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
4076
4080
  }
4077
- const readme = path11.join(runtimeDir, "README.md");
4081
+ const readme = path12.join(runtimeDir, "README.md");
4078
4082
  if (!existsSync11(readme)) {
4079
4083
  await writeFile7(readme, RUNTIME_README_BODY, "utf8");
4080
4084
  }
4081
4085
  }
4082
4086
  async function ensureAiCacheLayout(cacheDir) {
4083
4087
  await mkdir6(cacheDir, { recursive: true });
4084
- const gi = path11.join(cacheDir, ".gitignore");
4088
+ const gi = path12.join(cacheDir, ".gitignore");
4085
4089
  if (!existsSync11(gi)) {
4086
4090
  await writeFile7(gi, "*\n!.gitignore\n", "utf8");
4087
4091
  }
4088
4092
  }
4089
4093
  async function ensureGitignoreEntries(root, patterns) {
4090
4094
  try {
4091
- const gitignorePath = path11.join(root, ".gitignore");
4095
+ const gitignorePath = path12.join(root, ".gitignore");
4092
4096
  let existing = "";
4093
4097
  if (existsSync11(gitignorePath)) {
4094
4098
  existing = await readFile6(gitignorePath, "utf8");
@@ -4105,14 +4109,14 @@ async function ensureGitignoreEntries(root, patterns) {
4105
4109
  // src/commands/install-hooks.ts
4106
4110
  import { mkdir as mkdir8, writeFile as writeFile9, chmod, readFile as readFile8 } from "fs/promises";
4107
4111
  import { existsSync as existsSync13 } from "fs";
4108
- import path13 from "path";
4112
+ import path14 from "path";
4109
4113
  import "commander";
4110
4114
  import { findProjectRoot as findProjectRoot8 } from "@hiveai/core";
4111
4115
 
4112
4116
  // src/utils/claude-hooks.ts
4113
4117
  import { existsSync as existsSync12 } from "fs";
4114
4118
  import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
4115
- import path12 from "path";
4119
+ import path13 from "path";
4116
4120
  var HAIVE_HOOK_TAG = "haive-enforcement";
4117
4121
  var POST_TOOL_USE_GROUP = {
4118
4122
  matcher: "Edit|Write|Bash",
@@ -4208,7 +4212,7 @@ async function installClaudeHooksAtPath(settingsPath) {
4208
4212
  created = true;
4209
4213
  }
4210
4214
  const patched = patchClaudeSettings(raw);
4211
- await mkdir7(path12.dirname(settingsPath), { recursive: true });
4215
+ await mkdir7(path13.dirname(settingsPath), { recursive: true });
4212
4216
  await writeFile8(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
4213
4217
  return { settingsPath, created };
4214
4218
  }
@@ -4224,9 +4228,9 @@ async function uninstallClaudeHooksAtPath(settingsPath) {
4224
4228
  function defaultClaudeSettingsPath(scope, projectRoot) {
4225
4229
  if (scope === "user") {
4226
4230
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4227
- return path12.join(home, ".claude", "settings.json");
4231
+ return path13.join(home, ".claude", "settings.json");
4228
4232
  }
4229
- return path12.join(projectRoot, ".claude", "settings.local.json");
4233
+ return path13.join(projectRoot, ".claude", "settings.local.json");
4230
4234
  }
4231
4235
 
4232
4236
  // src/commands/install-hooks.ts
@@ -4283,18 +4287,18 @@ fi
4283
4287
  ];
4284
4288
  async function installGitHooks(opts) {
4285
4289
  const root = findProjectRoot8(opts.dir);
4286
- const gitDir = path13.join(root, ".git");
4290
+ const gitDir = path14.join(root, ".git");
4287
4291
  if (!existsSync13(gitDir)) {
4288
4292
  ui.error(`No .git directory at ${root}.`);
4289
4293
  process.exitCode = 1;
4290
4294
  return;
4291
4295
  }
4292
- const hooksDir = path13.join(gitDir, "hooks");
4296
+ const hooksDir = path14.join(gitDir, "hooks");
4293
4297
  await mkdir8(hooksDir, { recursive: true });
4294
4298
  let installed = 0;
4295
4299
  let skipped = 0;
4296
4300
  for (const { name, body } of HOOKS) {
4297
- const file = path13.join(hooksDir, name);
4301
+ const file = path14.join(hooksDir, name);
4298
4302
  if (existsSync13(file) && !opts.force) {
4299
4303
  const existing = await readFile8(file, "utf8");
4300
4304
  if (!existing.includes(HOOK_MARKER)) {
@@ -4360,7 +4364,7 @@ function registerInstallHooks(program2) {
4360
4364
  // src/commands/observe.ts
4361
4365
  import { appendFile, mkdir as mkdir9 } from "fs/promises";
4362
4366
  import { existsSync as existsSync14 } from "fs";
4363
- import path14 from "path";
4367
+ import path15 from "path";
4364
4368
  import "commander";
4365
4369
  import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
4366
4370
  var MAX_STDIN_BYTES = 256 * 1024;
@@ -4478,10 +4482,10 @@ function registerObserve(program2) {
4478
4482
  files: extractFiles(payload),
4479
4483
  ...failureHint ? { failure_hint: true } : {}
4480
4484
  };
4481
- const cacheDir = path14.join(paths.haiveDir, ".cache");
4485
+ const cacheDir = path15.join(paths.haiveDir, ".cache");
4482
4486
  await mkdir9(cacheDir, { recursive: true });
4483
4487
  await appendFile(
4484
- path14.join(cacheDir, "observations.jsonl"),
4488
+ path15.join(cacheDir, "observations.jsonl"),
4485
4489
  JSON.stringify(observation) + "\n",
4486
4490
  "utf8"
4487
4491
  );
@@ -4500,7 +4504,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4500
4504
  import { findProjectRoot as findProjectRoot10, resolveHaivePaths as resolveHaivePaths8 } from "@hiveai/core";
4501
4505
  import { mkdir as mkdir10, writeFile as writeFile10 } from "fs/promises";
4502
4506
  import { existsSync as existsSync15 } from "fs";
4503
- import path15 from "path";
4507
+ import path16 from "path";
4504
4508
  import { z } from "zod";
4505
4509
  import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
4506
4510
  import { existsSync as existsSync22 } from "fs";
@@ -4670,6 +4674,7 @@ import path82 from "path";
4670
4674
  import { execSync } from "child_process";
4671
4675
  import { readFile as readFile52, writeFile as writeFile13 } from "fs/promises";
4672
4676
  import { existsSync as existsSync21 } from "fs";
4677
+ import path112 from "path";
4673
4678
  import {
4674
4679
  allocateBudget,
4675
4680
  briefingProofLine,
@@ -4729,7 +4734,7 @@ import { z as z23 } from "zod";
4729
4734
  import { z as z24 } from "zod";
4730
4735
  import { existsSync as existsSync24 } from "fs";
4731
4736
  import { spawn } from "child_process";
4732
- import path112 from "path";
4737
+ import path122 from "path";
4733
4738
  import {
4734
4739
  deriveConfidence as deriveConfidence5,
4735
4740
  getUsage as getUsage7,
@@ -4788,7 +4793,7 @@ import { z as z29 } from "zod";
4788
4793
  import { z as z30 } from "zod";
4789
4794
  import { mkdir as mkdir82, writeFile as writeFile14 } from "fs/promises";
4790
4795
  import { existsSync as existsSync29 } from "fs";
4791
- import path122 from "path";
4796
+ import path132 from "path";
4792
4797
  import { execSync as execSync2 } from "child_process";
4793
4798
  import {
4794
4799
  buildFrontmatter as buildFrontmatter5,
@@ -4834,14 +4839,14 @@ var BootstrapProjectSaveInputSchema = {
4834
4839
  overwrite: z.boolean().default(false).describe("Overwrite an existing file instead of failing")
4835
4840
  };
4836
4841
  async function bootstrapProjectSave(input, ctx) {
4837
- const target = input.module ? path15.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4842
+ const target = input.module ? path16.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
4838
4843
  const exists = existsSync15(target);
4839
4844
  if (exists && !input.overwrite) {
4840
4845
  throw new Error(
4841
4846
  `${target} already exists. Pass overwrite=true to replace it.`
4842
4847
  );
4843
4848
  }
4844
- await mkdir10(path15.dirname(target), { recursive: true });
4849
+ await mkdir10(path16.dirname(target), { recursive: true });
4845
4850
  await writeFile10(target, input.content, "utf8");
4846
4851
  return {
4847
4852
  file_path: target,
@@ -6579,6 +6584,7 @@ async function getBriefing(input, ctx) {
6579
6584
  const topSymbols = Object.entries(codeMap.files).flatMap(
6580
6585
  ([fp, entry]) => entry.exports.slice(0, 3).map((e) => `${e.name} (${fp.split("/").slice(-2).join("/")})`)
6581
6586
  ).slice(0, 15).join(", ");
6587
+ const commands = await detectRunCommands(ctx.paths.root);
6582
6588
  projectContext = `# Project context (auto-generated by hAIve)
6583
6589
 
6584
6590
  > \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 +6594,10 @@ async function getBriefing(input, ctx) {
6588
6594
  - **Main file types:** ${topExts}
6589
6595
  - **Generated at:** ${codeMap.generated_at}
6590
6596
 
6591
- ## Key exports (sample)
6597
+ ` + (commands ? `## Commands
6598
+ ${commands}
6599
+
6600
+ ` : "") + `## Key exports (sample)
6592
6601
  ` + topSymbols + "\n";
6593
6602
  autoContextGenerated = true;
6594
6603
  setupWarnings.push(
@@ -6868,6 +6877,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
6868
6877
  }
6869
6878
  };
6870
6879
  }
6880
+ async function detectRunCommands(root) {
6881
+ const pkgPath = path112.join(root, "package.json");
6882
+ if (!existsSync21(pkgPath)) return null;
6883
+ try {
6884
+ const pkg = JSON.parse(await readFile52(pkgPath, "utf8"));
6885
+ const scripts = pkg.scripts ?? {};
6886
+ const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
6887
+ const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
6888
+ return lines.length > 0 ? lines.join("\n") : null;
6889
+ } catch {
6890
+ return null;
6891
+ }
6892
+ }
6871
6893
  var CodeMapInputSchema = {
6872
6894
  file: z20.string().optional().describe("Filter to files whose path contains this substring"),
6873
6895
  symbol: z20.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
@@ -7091,7 +7113,7 @@ var WhyThisFileInputSchema = {
7091
7113
  memory_limit: z25.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
7092
7114
  };
7093
7115
  async function whyThisFile(input, ctx) {
7094
- const fileExists = existsSync24(path112.join(ctx.paths.root, input.path));
7116
+ const fileExists = existsSync24(path122.join(ctx.paths.root, input.path));
7095
7117
  const [commits, memories, codeMap] = await Promise.all([
7096
7118
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
7097
7119
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -8123,13 +8145,13 @@ async function patternDetect(input, ctx) {
8123
8145
  try {
8124
8146
  const changedFiles = gitChangedFiles(ctx.paths.root, input.since_days);
8125
8147
  const configFiles = changedFiles.filter(
8126
- (f) => CONFIG_PATTERNS.some((p) => path122.basename(f.toLowerCase()).includes(p))
8148
+ (f) => CONFIG_PATTERNS.some((p) => path132.basename(f.toLowerCase()).includes(p))
8127
8149
  );
8128
8150
  for (const file of configFiles.slice(0, 5)) {
8129
8151
  const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
8130
8152
  if (!diff) continue;
8131
- const parentDir = path122.basename(path122.dirname(file));
8132
- const baseName = path122.basename(file).replace(/\.[^.]+$/, "");
8153
+ const parentDir = path132.basename(path132.dirname(file));
8154
+ const baseName = path132.basename(file).replace(/\.[^.]+$/, "");
8133
8155
  const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
8134
8156
  matches.push({
8135
8157
  kind: "config_change",
@@ -8193,7 +8215,7 @@ async function patternDetect(input, ctx) {
8193
8215
  for (const [p, { count, tools }] of pathCounts) {
8194
8216
  if (count < HOT_FILE_MIN) continue;
8195
8217
  if (tools.has("mem_tried") || tools.has("mem_observe")) continue;
8196
- if (CONFIG_PATTERNS.some((cp2) => path122.basename(p).includes(cp2))) continue;
8218
+ if (CONFIG_PATTERNS.some((cp2) => path132.basename(p).includes(cp2))) continue;
8197
8219
  const slug = p.replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").slice(0, 40);
8198
8220
  matches.push({
8199
8221
  kind: "hot_file",
@@ -8240,7 +8262,7 @@ async function patternDetect(input, ctx) {
8240
8262
  void 0
8241
8263
  );
8242
8264
  if (existsSync29(file)) continue;
8243
- await mkdir82(path122.dirname(file), { recursive: true });
8265
+ await mkdir82(path132.dirname(file), { recursive: true });
8244
8266
  await writeFile14(
8245
8267
  file,
8246
8268
  serializeMemory12({ frontmatter: fm, body: match.proposed_body }),
@@ -8654,7 +8676,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
8654
8676
  };
8655
8677
  }
8656
8678
  var SERVER_NAME = "haive";
8657
- var SERVER_VERSION = "0.23.0";
8679
+ var SERVER_VERSION = "0.24.0";
8658
8680
  function jsonResult(data) {
8659
8681
  return {
8660
8682
  content: [
@@ -9688,7 +9710,7 @@ function registerMcp(program2) {
9688
9710
  import { spawnSync as spawnSync4 } from "child_process";
9689
9711
  import { readFile as readFile10, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
9690
9712
  import { existsSync as existsSync33 } from "fs";
9691
- import path16 from "path";
9713
+ import path17 from "path";
9692
9714
  import "commander";
9693
9715
  import {
9694
9716
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
@@ -9882,10 +9904,10 @@ function registerSync(program2) {
9882
9904
  const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
9883
9905
  let bridgeTargets;
9884
9906
  if (opts.bridgeFile) {
9885
- bridgeTargets = [path16.resolve(opts.bridgeFile)];
9907
+ bridgeTargets = [path17.resolve(opts.bridgeFile)];
9886
9908
  } else {
9887
- const agentsMd = path16.join(root, "AGENTS.md");
9888
- bridgeTargets = [path16.join(root, "CLAUDE.md")];
9909
+ const agentsMd = path17.join(root, "AGENTS.md");
9910
+ bridgeTargets = [path17.join(root, "CLAUDE.md")];
9889
9911
  if (existsSync33(agentsMd)) bridgeTargets.push(agentsMd);
9890
9912
  }
9891
9913
  for (const bridgeFile of bridgeTargets) {
@@ -10017,10 +10039,10 @@ Wait for **explicit confirmation** before acting.
10017
10039
  topic: `dep-bump-${slugParts}`
10018
10040
  });
10019
10041
  if (!dryRun) {
10020
- const teamDir = path16.join(paths.memoriesDir, "team");
10042
+ const teamDir = path17.join(paths.memoriesDir, "team");
10021
10043
  await mkdir11(teamDir, { recursive: true });
10022
10044
  await writeFile15(
10023
- path16.join(teamDir, `${fm.id}.md`),
10045
+ path17.join(teamDir, `${fm.id}.md`),
10024
10046
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
10025
10047
  "utf8"
10026
10048
  );
@@ -10086,10 +10108,10 @@ Wait for **explicit confirmation** before acting.
10086
10108
  topic: `contract-breaking-${diff.contract}`
10087
10109
  });
10088
10110
  if (!dryRun) {
10089
- const teamDir = path16.join(paths.memoriesDir, "team");
10111
+ const teamDir = path17.join(paths.memoriesDir, "team");
10090
10112
  await mkdir11(teamDir, { recursive: true });
10091
10113
  await writeFile15(
10092
- path16.join(teamDir, `${fm.id}.md`),
10114
+ path17.join(teamDir, `${fm.id}.md`),
10093
10115
  serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
10094
10116
  "utf8"
10095
10117
  );
@@ -10212,11 +10234,11 @@ ${BRIDGE_END}`;
10212
10234
  const startIdx = existing.indexOf(BRIDGE_START);
10213
10235
  const endIdx = existing.indexOf(BRIDGE_END);
10214
10236
  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.`);
10237
+ ui.warn(`${path17.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
10216
10238
  return;
10217
10239
  }
10218
10240
  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.`);
10241
+ ui.warn(`${path17.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
10220
10242
  return;
10221
10243
  }
10222
10244
  let updated;
@@ -10224,14 +10246,14 @@ ${BRIDGE_END}`;
10224
10246
  updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
10225
10247
  } else {
10226
10248
  if (!fileExists && !quiet) {
10227
- ui.info(`Creating ${path16.relative(root, bridgeFile)} with haive memory block.`);
10249
+ ui.info(`Creating ${path17.relative(root, bridgeFile)} with haive memory block.`);
10228
10250
  }
10229
10251
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
10230
10252
  }
10231
10253
  await writeFile15(bridgeFile, updated, "utf8");
10232
10254
  if (!quiet) {
10233
10255
  console.log(
10234
- ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path16.relative(root, bridgeFile)}`)
10256
+ ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path17.relative(root, bridgeFile)}`)
10235
10257
  );
10236
10258
  }
10237
10259
  }
@@ -10258,7 +10280,7 @@ function collectSinceChanges(root, ref) {
10258
10280
  import { createHash as createHash2 } from "crypto";
10259
10281
  import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile16 } from "fs/promises";
10260
10282
  import { existsSync as existsSync34 } from "fs";
10261
- import path17 from "path";
10283
+ import path18 from "path";
10262
10284
  import "commander";
10263
10285
  import {
10264
10286
  buildFrontmatter as buildFrontmatter7,
@@ -10316,7 +10338,7 @@ function registerMemoryAdd(memory2) {
10316
10338
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
10317
10339
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
10318
10340
  if (anchorPaths.length > 0) {
10319
- const missing = anchorPaths.filter((p) => !existsSync34(path17.resolve(root, p)));
10341
+ const missing = anchorPaths.filter((p) => !existsSync34(path18.resolve(root, p)));
10320
10342
  if (missing.length > 0) {
10321
10343
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
10322
10344
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -10380,7 +10402,7 @@ TODO \u2014 write the memory body.
10380
10402
  const suggestedSensor = !newFrontmatter.sensor ? suggestSensorForCliMemory(opts.type, body, newFrontmatter.anchor.paths) : null;
10381
10403
  if (suggestedSensor) newFrontmatter.sensor = suggestedSensor;
10382
10404
  await writeFile16(topicMatch.filePath, serializeMemory14({ frontmatter: newFrontmatter, body }), "utf8");
10383
- ui.success(`Updated (topic upsert) ${path17.relative(root, topicMatch.filePath)}`);
10405
+ ui.success(`Updated (topic upsert) ${path18.relative(root, topicMatch.filePath)}`);
10384
10406
  ui.info(`id=${fm.id} revision=${revisionCount}`);
10385
10407
  if (suggestedSensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(suggestedSensor.pattern)}`);
10386
10408
  await runPostMemoryAutopilot(root, paths, config);
@@ -10404,7 +10426,7 @@ TODO \u2014 write the memory body.
10404
10426
  activation
10405
10427
  });
10406
10428
  const file = memoryFilePath7(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
10407
- await mkdir12(path17.dirname(file), { recursive: true });
10429
+ await mkdir12(path18.dirname(file), { recursive: true });
10408
10430
  if (existsSync34(file)) {
10409
10431
  ui.error(`Memory already exists at ${file}`);
10410
10432
  process.exitCode = 1;
@@ -10423,7 +10445,7 @@ TODO \u2014 write the memory body.
10423
10445
  }
10424
10446
  }
10425
10447
  await writeFile16(file, serializeMemory14({ frontmatter, body }), "utf8");
10426
- ui.success(`Created ${path17.relative(root, file)}`);
10448
+ ui.success(`Created ${path18.relative(root, file)}`);
10427
10449
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
10428
10450
  if (frontmatter.sensor?.autogen) {
10429
10451
  ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(frontmatter.sensor.pattern)}`);
@@ -10503,7 +10525,7 @@ function slugify(value) {
10503
10525
 
10504
10526
  // src/commands/memory-list.ts
10505
10527
  import { existsSync as existsSync35 } from "fs";
10506
- import path18 from "path";
10528
+ import path19 from "path";
10507
10529
  import "commander";
10508
10530
  import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
10509
10531
 
@@ -10556,7 +10578,7 @@ function registerMemoryList(memory2) {
10556
10578
  );
10557
10579
  const title = mem.body.match(/^#\s+(.+)$/m)?.[1]?.trim();
10558
10580
  if (title && title !== fm.id) console.log(` ${title}`);
10559
- console.log(` ${ui.dim(path18.relative(root, filePath))}`);
10581
+ console.log(` ${ui.dim(path19.relative(root, filePath))}`);
10560
10582
  }
10561
10583
  const totalLabel = clipped > 0 ? `
10562
10584
  ${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
@@ -10595,7 +10617,7 @@ function matchesFilters(loaded, opts) {
10595
10617
  // src/commands/memory-promote.ts
10596
10618
  import { mkdir as mkdir13, unlink as unlink2, writeFile as writeFile17 } from "fs/promises";
10597
10619
  import { existsSync as existsSync36 } from "fs";
10598
- import path19 from "path";
10620
+ import path20 from "path";
10599
10621
  import "commander";
10600
10622
  import {
10601
10623
  findProjectRoot as findProjectRoot15,
@@ -10642,11 +10664,11 @@ function registerMemoryPromote(memory2) {
10642
10664
  body: found.memory.body
10643
10665
  };
10644
10666
  const newPath = memoryFilePath8(paths, "team", updated.frontmatter.id);
10645
- await mkdir13(path19.dirname(newPath), { recursive: true });
10667
+ await mkdir13(path20.dirname(newPath), { recursive: true });
10646
10668
  await writeFile17(newPath, serializeMemory15(updated), "utf8");
10647
10669
  await unlink2(found.filePath);
10648
10670
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
10649
- ui.info(`Now at ${path19.relative(root, newPath)}`);
10671
+ ui.info(`Now at ${path20.relative(root, newPath)}`);
10650
10672
  console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
10651
10673
  });
10652
10674
  }
@@ -10654,7 +10676,7 @@ function registerMemoryPromote(memory2) {
10654
10676
  // src/commands/memory-approve.ts
10655
10677
  import { existsSync as existsSync37 } from "fs";
10656
10678
  import { writeFile as writeFile18 } from "fs/promises";
10657
- import path20 from "path";
10679
+ import path21 from "path";
10658
10680
  import "commander";
10659
10681
  import {
10660
10682
  findProjectRoot as findProjectRoot16,
@@ -10718,14 +10740,14 @@ function registerMemoryApprove(memory2) {
10718
10740
  };
10719
10741
  await writeFile18(found.filePath, serializeMemory16(next), "utf8");
10720
10742
  ui.success(`Approved ${id} (status=validated)`);
10721
- ui.info(path20.relative(root, found.filePath));
10743
+ ui.info(path21.relative(root, found.filePath));
10722
10744
  });
10723
10745
  }
10724
10746
 
10725
10747
  // src/commands/memory-update.ts
10726
10748
  import { readFile as readFile12, writeFile as writeFile19 } from "fs/promises";
10727
10749
  import { existsSync as existsSync38 } from "fs";
10728
- import path21 from "path";
10750
+ import path23 from "path";
10729
10751
  import "commander";
10730
10752
  import {
10731
10753
  findProjectRoot as findProjectRoot17,
@@ -10804,7 +10826,7 @@ function registerMemoryUpdate(memory2) {
10804
10826
  serializeMemory17({ frontmatter: newFrontmatter, body: newBody }),
10805
10827
  "utf8"
10806
10828
  );
10807
- ui.success(`Updated ${path21.relative(root, loaded.filePath)}`);
10829
+ ui.success(`Updated ${path23.relative(root, loaded.filePath)}`);
10808
10830
  ui.info(`fields: ${updated.join(", ")}`);
10809
10831
  });
10810
10832
  }
@@ -10825,7 +10847,7 @@ function parseCsv3(value) {
10825
10847
  // src/commands/memory-auto-promote.ts
10826
10848
  import { writeFile as writeFile20 } from "fs/promises";
10827
10849
  import { existsSync as existsSync39 } from "fs";
10828
- import path23 from "path";
10850
+ import path24 from "path";
10829
10851
  import "commander";
10830
10852
  import {
10831
10853
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
@@ -10870,7 +10892,7 @@ function registerMemoryAutoPromote(memory2) {
10870
10892
  console.log(
10871
10893
  `${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
10872
10894
  );
10873
- console.log(` ${ui.dim(path23.relative(root, filePath))}`);
10895
+ console.log(` ${ui.dim(path24.relative(root, filePath))}`);
10874
10896
  if (opts.apply) {
10875
10897
  const next = {
10876
10898
  frontmatter: { ...mem.frontmatter, status: "validated" },
@@ -10889,7 +10911,7 @@ function registerMemoryAutoPromote(memory2) {
10889
10911
  import { spawn as spawn3 } from "child_process";
10890
10912
  import { existsSync as existsSync40 } from "fs";
10891
10913
  import { readFile as readFile13 } from "fs/promises";
10892
- import path24 from "path";
10914
+ import path25 from "path";
10893
10915
  import "commander";
10894
10916
  import {
10895
10917
  findProjectRoot as findProjectRoot19,
@@ -10913,7 +10935,7 @@ function registerMemoryEdit(memory2) {
10913
10935
  return;
10914
10936
  }
10915
10937
  const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
10916
- ui.info(`Opening ${path24.relative(root, found.filePath)} with ${editor}\u2026`);
10938
+ ui.info(`Opening ${path25.relative(root, found.filePath)} with ${editor}\u2026`);
10917
10939
  const code = await runEditor(editor, found.filePath);
10918
10940
  if (code !== 0) {
10919
10941
  ui.warn(`Editor exited with status ${code}.`);
@@ -10941,7 +10963,7 @@ function runEditor(editor, file) {
10941
10963
 
10942
10964
  // src/commands/memory-for-files.ts
10943
10965
  import { existsSync as existsSync41 } from "fs";
10944
- import path25 from "path";
10966
+ import path26 from "path";
10945
10967
  import "commander";
10946
10968
  import {
10947
10969
  deriveConfidence as deriveConfidence9,
@@ -11063,13 +11085,13 @@ function printGroup(root, label, loaded, usage) {
11063
11085
  const u = getUsage13(usage, fm.id);
11064
11086
  const conf = deriveConfidence9(fm, u);
11065
11087
  console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
11066
- console.log(` ${ui.dim(path25.relative(root, filePath))}`);
11088
+ console.log(` ${ui.dim(path26.relative(root, filePath))}`);
11067
11089
  }
11068
11090
  }
11069
11091
 
11070
11092
  // src/commands/memory-hot.ts
11071
11093
  import { existsSync as existsSync43 } from "fs";
11072
- import path26 from "path";
11094
+ import path27 from "path";
11073
11095
  import "commander";
11074
11096
  import {
11075
11097
  findProjectRoot as findProjectRoot21,
@@ -11111,7 +11133,7 @@ function registerMemoryHot(memory2) {
11111
11133
  console.log(
11112
11134
  `${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
11135
  );
11114
- console.log(` ${ui.dim(path26.relative(root, filePath))}`);
11136
+ console.log(` ${ui.dim(path27.relative(root, filePath))}`);
11115
11137
  }
11116
11138
  ui.info(
11117
11139
  `${candidates.length} hot (read \u2265${threshold}\xD7) \u2014 agents rely on these; promote with \`haive memory promote <id>\`.
@@ -11123,7 +11145,7 @@ function registerMemoryHot(memory2) {
11123
11145
  // src/commands/memory-tried.ts
11124
11146
  import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
11125
11147
  import { existsSync as existsSync44 } from "fs";
11126
- import path27 from "path";
11148
+ import path28 from "path";
11127
11149
  import "commander";
11128
11150
  import {
11129
11151
  buildFrontmatter as buildFrontmatter8,
@@ -11179,14 +11201,14 @@ function registerMemoryTried(memory2) {
11179
11201
  frontmatter.sensor = sensor;
11180
11202
  }
11181
11203
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
11182
- await mkdir14(path27.dirname(file), { recursive: true });
11204
+ await mkdir14(path28.dirname(file), { recursive: true });
11183
11205
  if (existsSync44(file)) {
11184
11206
  ui.error(`Memory already exists at ${file}`);
11185
11207
  process.exitCode = 1;
11186
11208
  return;
11187
11209
  }
11188
11210
  await writeFile21(file, serializeMemory19({ frontmatter, body }), "utf8");
11189
- ui.success(`Recorded: ${path27.relative(root, file)}`);
11211
+ ui.success(`Recorded: ${path28.relative(root, file)}`);
11190
11212
  ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
11191
11213
  if (sensor) {
11192
11214
  ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(sensor.pattern)}`);
@@ -11205,7 +11227,7 @@ function parseCsv4(value) {
11205
11227
  // src/commands/memory-seed.ts
11206
11228
  import { readFile as readFile14 } from "fs/promises";
11207
11229
  import { existsSync as existsSync45 } from "fs";
11208
- import path28 from "path";
11230
+ import path29 from "path";
11209
11231
  import "commander";
11210
11232
  import {
11211
11233
  findProjectRoot as findProjectRoot23,
@@ -11214,7 +11236,7 @@ import {
11214
11236
  } from "@hiveai/core";
11215
11237
  async function readDependencyMap(root) {
11216
11238
  try {
11217
- const raw = await readFile14(path28.join(root, "package.json"), "utf8");
11239
+ const raw = await readFile14(path29.join(root, "package.json"), "utf8");
11218
11240
  const pkg = JSON.parse(raw);
11219
11241
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
11220
11242
  } catch {
@@ -11301,7 +11323,7 @@ function registerMemorySeed(memory2) {
11301
11323
 
11302
11324
  // src/commands/memory-pending.ts
11303
11325
  import { existsSync as existsSync46 } from "fs";
11304
- import path29 from "path";
11326
+ import path30 from "path";
11305
11327
  import "commander";
11306
11328
  import {
11307
11329
  findProjectRoot as findProjectRoot24,
@@ -11347,7 +11369,7 @@ function registerMemoryPending(memory2) {
11347
11369
  console.log(
11348
11370
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
11349
11371
  );
11350
- console.log(` ${ui.dim(path29.relative(root, filePath))}`);
11372
+ console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11351
11373
  }
11352
11374
  if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
11353
11375
  console.log();
@@ -11362,7 +11384,7 @@ function registerMemoryPending(memory2) {
11362
11384
  console.log(
11363
11385
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
11364
11386
  );
11365
- console.log(` ${ui.dim(path29.relative(root, filePath))}`);
11387
+ console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11366
11388
  }
11367
11389
  console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
11368
11390
  }
@@ -11372,7 +11394,7 @@ function registerMemoryPending(memory2) {
11372
11394
 
11373
11395
  // src/commands/memory-query.ts
11374
11396
  import { existsSync as existsSync47 } from "fs";
11375
- import path30 from "path";
11397
+ import path31 from "path";
11376
11398
  import "commander";
11377
11399
  import {
11378
11400
  extractSnippet as extractSnippet2,
@@ -11429,7 +11451,7 @@ function registerMemoryQuery(memory2) {
11429
11451
  const fm = mem.frontmatter;
11430
11452
  const statusBadge = ui.statusBadge(fm.status);
11431
11453
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
11432
- console.log(` ${ui.dim(path30.relative(root, filePath))}`);
11454
+ console.log(` ${ui.dim(path31.relative(root, filePath))}`);
11433
11455
  const snippet = extractSnippet2(mem.body, snippetNeedle);
11434
11456
  if (snippet) console.log(` ${snippet}`);
11435
11457
  }
@@ -11499,7 +11521,7 @@ function registerMemoryReject(memory2) {
11499
11521
  // src/commands/memory-rm.ts
11500
11522
  import { existsSync as existsSync49 } from "fs";
11501
11523
  import { unlink as unlink3 } from "fs/promises";
11502
- import path31 from "path";
11524
+ import path33 from "path";
11503
11525
  import { createInterface as createInterface2 } from "readline/promises";
11504
11526
  import "commander";
11505
11527
  import {
@@ -11524,7 +11546,7 @@ function registerMemoryRm(memory2) {
11524
11546
  process.exitCode = 1;
11525
11547
  return;
11526
11548
  }
11527
- const rel = path31.relative(root, found.filePath);
11549
+ const rel = path33.relative(root, found.filePath);
11528
11550
  if (!opts.yes) {
11529
11551
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
11530
11552
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
@@ -11550,7 +11572,7 @@ function registerMemoryRm(memory2) {
11550
11572
  // src/commands/memory-show.ts
11551
11573
  import { existsSync as existsSync50 } from "fs";
11552
11574
  import { readFile as readFile15 } from "fs/promises";
11553
- import path33 from "path";
11575
+ import path34 from "path";
11554
11576
  import "commander";
11555
11577
  import {
11556
11578
  deriveConfidence as deriveConfidence10,
@@ -11592,7 +11614,7 @@ function registerMemoryShow(memory2) {
11592
11614
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
11593
11615
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
11594
11616
  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)}`);
11617
+ console.log(`${ui.dim("file:")} ${path34.relative(root, found.filePath)}`);
11596
11618
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
11597
11619
  console.log(ui.dim("anchor:"));
11598
11620
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -11608,7 +11630,7 @@ function registerMemoryShow(memory2) {
11608
11630
 
11609
11631
  // src/commands/memory-stats.ts
11610
11632
  import { existsSync as existsSync51 } from "fs";
11611
- import path34 from "path";
11633
+ import path35 from "path";
11612
11634
  import "commander";
11613
11635
  import {
11614
11636
  deriveConfidence as deriveConfidence11,
@@ -11646,7 +11668,7 @@ function registerMemoryStats(memory2) {
11646
11668
  console.log(
11647
11669
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
11648
11670
  );
11649
- console.log(` ${ui.dim(path34.relative(root, filePath))}`);
11671
+ console.log(` ${ui.dim(path35.relative(root, filePath))}`);
11650
11672
  }
11651
11673
  });
11652
11674
  }
@@ -11809,7 +11831,7 @@ function registerMemoryFeedback(memory2) {
11809
11831
  // src/commands/memory-verify.ts
11810
11832
  import { writeFile as writeFile25 } from "fs/promises";
11811
11833
  import { existsSync as existsSync55 } from "fs";
11812
- import path35 from "path";
11834
+ import path36 from "path";
11813
11835
  import "commander";
11814
11836
  import {
11815
11837
  findProjectRoot as findProjectRoot32,
@@ -11851,7 +11873,7 @@ function registerMemoryVerify(memory2) {
11851
11873
  for (const { memory: mem, filePath } of targets) {
11852
11874
  const result = await verifyAnchor3(mem, { projectRoot: root });
11853
11875
  const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
11854
- const rel = path35.relative(root, filePath);
11876
+ const rel = path36.relative(root, filePath);
11855
11877
  if (!isAnchored) {
11856
11878
  anchorlessIds.push(mem.frontmatter.id);
11857
11879
  entries.push({ id: mem.frontmatter.id, status: "anchorless", path: rel });
@@ -11996,7 +12018,7 @@ function registerMemoryImport(memory2) {
11996
12018
  // src/commands/memory-import-changelog.ts
11997
12019
  import { existsSync as existsSync57 } from "fs";
11998
12020
  import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile26 } from "fs/promises";
11999
- import path36 from "path";
12021
+ import path37 from "path";
12000
12022
  import "commander";
12001
12023
  import {
12002
12024
  buildFrontmatter as buildFrontmatter9,
@@ -12068,7 +12090,7 @@ function registerMemoryImportChangelog(memory2) {
12068
12090
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
12069
12091
  const root = findProjectRoot34(opts.dir);
12070
12092
  const paths = resolveHaivePaths31(root);
12071
- const changelogPath = path36.resolve(root, opts.fromChangelog);
12093
+ const changelogPath = path37.resolve(root, opts.fromChangelog);
12072
12094
  if (!existsSync57(changelogPath)) {
12073
12095
  ui.error(`CHANGELOG not found: ${changelogPath}`);
12074
12096
  process.exitCode = 1;
@@ -12089,9 +12111,9 @@ function registerMemoryImportChangelog(memory2) {
12089
12111
  entries = entries.filter((e) => requested.includes(e.version));
12090
12112
  }
12091
12113
  }
12092
- const pkgName = opts.package ?? path36.basename(path36.dirname(changelogPath));
12114
+ const pkgName = opts.package ?? path37.basename(path37.dirname(changelogPath));
12093
12115
  const scope = opts.scope ?? "team";
12094
- const teamDir = path36.join(paths.memoriesDir, scope);
12116
+ const teamDir = path37.join(paths.memoriesDir, scope);
12095
12117
  await mkdir15(teamDir, { recursive: true });
12096
12118
  let saved = 0;
12097
12119
  for (const entry of entries) {
@@ -12114,7 +12136,7 @@ function registerMemoryImportChangelog(memory2) {
12114
12136
  lines.push("");
12115
12137
  }
12116
12138
  lines.push(
12117
- `**Source:** \`${path36.relative(root, changelogPath)}\`
12139
+ `**Source:** \`${path37.relative(root, changelogPath)}\`
12118
12140
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
12119
12141
  );
12120
12142
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -12129,11 +12151,11 @@ function registerMemoryImportChangelog(memory2) {
12129
12151
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
12130
12152
  `v${entry.version}`
12131
12153
  ],
12132
- paths: [path36.relative(root, changelogPath)],
12154
+ paths: [path37.relative(root, changelogPath)],
12133
12155
  topic: `changelog-${pkgName}-${entry.version}`
12134
12156
  });
12135
12157
  await writeFile26(
12136
- path36.join(teamDir, `${fm.id}.md`),
12158
+ path37.join(teamDir, `${fm.id}.md`),
12137
12159
  serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
12138
12160
  "utf8"
12139
12161
  );
@@ -12158,7 +12180,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
12158
12180
  // src/commands/memory-digest.ts
12159
12181
  import { existsSync as existsSync58 } from "fs";
12160
12182
  import { writeFile as writeFile27 } from "fs/promises";
12161
- import path37 from "path";
12183
+ import path38 from "path";
12162
12184
  import "commander";
12163
12185
  import {
12164
12186
  deriveConfidence as deriveConfidence12,
@@ -12253,7 +12275,7 @@ function registerMemoryDigest(program2) {
12253
12275
  );
12254
12276
  const digest = lines.join("\n");
12255
12277
  if (opts.out) {
12256
- const outPath = path37.resolve(process.cwd(), opts.out);
12278
+ const outPath = path38.resolve(process.cwd(), opts.out);
12257
12279
  await writeFile27(outPath, digest, "utf8");
12258
12280
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
12259
12281
  } else {
@@ -12266,7 +12288,7 @@ function registerMemoryDigest(program2) {
12266
12288
  import { writeFile as writeFile28, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
12267
12289
  import { existsSync as existsSync59 } from "fs";
12268
12290
  import { spawn as spawn4 } from "child_process";
12269
- import path38 from "path";
12291
+ import path39 from "path";
12270
12292
  import "commander";
12271
12293
  import {
12272
12294
  buildFrontmatter as buildFrontmatter10,
@@ -12281,7 +12303,7 @@ import {
12281
12303
  summarizeCaughtForYou
12282
12304
  } from "@hiveai/core";
12283
12305
  async function buildAutoRecap(paths) {
12284
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12306
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12285
12307
  if (!existsSync59(obsFile)) return await buildGitAutoRecap(paths);
12286
12308
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12287
12309
  if (!raw.trim()) return await buildGitAutoRecap(paths);
@@ -12450,7 +12472,7 @@ function runGit(cwd, args) {
12450
12472
  });
12451
12473
  }
12452
12474
  async function observationStart(paths) {
12453
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12475
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12454
12476
  if (!existsSync59(obsFile)) return null;
12455
12477
  const raw = await readFile18(obsFile, "utf8").catch(() => "");
12456
12478
  let first = null;
@@ -12539,14 +12561,14 @@ function registerSessionEnd(session2) {
12539
12561
  });
12540
12562
  const topic = recapTopic2(scope, opts.module);
12541
12563
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
12542
- const missingPaths = filesTouched.filter((p) => !existsSync59(path38.resolve(root, p)));
12564
+ const missingPaths = filesTouched.filter((p) => !existsSync59(path39.resolve(root, p)));
12543
12565
  if (missingPaths.length > 0 && !opts.quiet) {
12544
12566
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
12545
12567
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
12546
12568
  }
12547
12569
  const cleanupObservations = async () => {
12548
12570
  if (!opts.auto) return;
12549
- const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
12571
+ const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
12550
12572
  if (existsSync59(obsFile)) await rm2(obsFile).catch(() => {
12551
12573
  });
12552
12574
  };
@@ -12571,7 +12593,7 @@ function registerSessionEnd(session2) {
12571
12593
  await cleanupObservations();
12572
12594
  if (!opts.quiet) {
12573
12595
  ui.success(`Session recap updated (revision #${revisionCount})`);
12574
- ui.info(`id=${fm.id} file=${path38.relative(root, topicMatch.filePath)}`);
12596
+ ui.info(`id=${fm.id} file=${path39.relative(root, topicMatch.filePath)}`);
12575
12597
  await printCaughtForYou(paths, caughtSince, opts.quiet);
12576
12598
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
12577
12599
  }
@@ -12589,12 +12611,12 @@ function registerSessionEnd(session2) {
12589
12611
  status: "validated"
12590
12612
  });
12591
12613
  const file = memoryFilePath10(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
12592
- await mkdir16(path38.dirname(file), { recursive: true });
12614
+ await mkdir16(path39.dirname(file), { recursive: true });
12593
12615
  await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
12594
12616
  await cleanupObservations();
12595
12617
  if (!opts.quiet) {
12596
12618
  ui.success(`Session recap created`);
12597
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path38.relative(root, file)}`);
12619
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path39.relative(root, file)}`);
12598
12620
  await printCaughtForYou(paths, caughtSince, opts.quiet);
12599
12621
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
12600
12622
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
@@ -12607,8 +12629,8 @@ function parseCsv5(value) {
12607
12629
  }
12608
12630
  function normalizeAnchorPath(root, filePath) {
12609
12631
  if (!filePath) return filePath;
12610
- if (!path38.isAbsolute(filePath)) return filePath;
12611
- const rel = path38.relative(root, filePath);
12632
+ if (!path39.isAbsolute(filePath)) return filePath;
12633
+ const rel = path39.relative(root, filePath);
12612
12634
  if (rel.startsWith("..")) return filePath;
12613
12635
  return rel;
12614
12636
  }
@@ -12616,7 +12638,7 @@ function normalizeAnchorPath(root, filePath) {
12616
12638
  // src/commands/snapshot.ts
12617
12639
  import { existsSync as existsSync60 } from "fs";
12618
12640
  import { readdir as readdir4 } from "fs/promises";
12619
- import path39 from "path";
12641
+ import path40 from "path";
12620
12642
  import "commander";
12621
12643
  import {
12622
12644
  diffContract,
@@ -12655,7 +12677,7 @@ function registerSnapshot(program2) {
12655
12677
  return;
12656
12678
  }
12657
12679
  if (opts.list) {
12658
- const contractsDir = path39.join(paths.haiveDir, "contracts");
12680
+ const contractsDir = path40.join(paths.haiveDir, "contracts");
12659
12681
  if (!existsSync60(contractsDir)) {
12660
12682
  console.log(ui.dim("No contract snapshots found."));
12661
12683
  return;
@@ -12711,7 +12733,7 @@ function registerSnapshot(program2) {
12711
12733
  return;
12712
12734
  }
12713
12735
  const contractPath = opts.contract;
12714
- const name = opts.name ?? path39.basename(contractPath, path39.extname(contractPath));
12736
+ const name = opts.name ?? path40.basename(contractPath, path40.extname(contractPath));
12715
12737
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
12716
12738
  const contract = { name, path: contractPath, format };
12717
12739
  try {
@@ -12766,8 +12788,8 @@ async function runDiff(root, haiveDir, contract) {
12766
12788
  }
12767
12789
  }
12768
12790
  function detectFormat(filePath) {
12769
- const ext = path39.extname(filePath).toLowerCase();
12770
- const base = path39.basename(filePath).toLowerCase();
12791
+ const ext = path40.extname(filePath).toLowerCase();
12792
+ const base = path40.basename(filePath).toLowerCase();
12771
12793
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
12772
12794
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
12773
12795
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -12782,7 +12804,7 @@ function detectFormat(filePath) {
12782
12804
  // src/commands/hub.ts
12783
12805
  import { existsSync as existsSync61 } from "fs";
12784
12806
  import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
12785
- import path40 from "path";
12807
+ import path41 from "path";
12786
12808
  import { spawnSync as spawnSync5 } from "child_process";
12787
12809
  import "commander";
12788
12810
  import {
@@ -12801,7 +12823,7 @@ function registerHub(program2) {
12801
12823
  hub.command("init <hubPath>").description(
12802
12824
  "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
12825
  ).action(async (hubPath) => {
12804
- const absPath = path40.resolve(hubPath);
12826
+ const absPath = path41.resolve(hubPath);
12805
12827
  await mkdir17(absPath, { recursive: true });
12806
12828
  const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
12807
12829
  if (gitCheck.status !== 0) {
@@ -12812,10 +12834,10 @@ function registerHub(program2) {
12812
12834
  return;
12813
12835
  }
12814
12836
  }
12815
- const sharedDir = path40.join(absPath, ".ai", "memories", "shared");
12837
+ const sharedDir = path41.join(absPath, ".ai", "memories", "shared");
12816
12838
  await mkdir17(sharedDir, { recursive: true });
12817
12839
  await writeFile29(
12818
- path40.join(absPath, ".ai", "README.md"),
12840
+ path41.join(absPath, ".ai", "README.md"),
12819
12841
  `# hAIve Team Knowledge Hub
12820
12842
 
12821
12843
  This repo is a shared knowledge hub for hAIve.
@@ -12837,7 +12859,7 @@ haive hub pull # import into a project
12837
12859
  "utf8"
12838
12860
  );
12839
12861
  await writeFile29(
12840
- path40.join(absPath, ".gitignore"),
12862
+ path41.join(absPath, ".gitignore"),
12841
12863
  ".ai/.cache/\n.ai/memories/personal/\n",
12842
12864
  "utf8"
12843
12865
  );
@@ -12852,7 +12874,7 @@ haive hub pull # import into a project
12852
12874
  `
12853
12875
  Next steps:
12854
12876
  1. Add hubPath to your project's .ai/haive.config.json:
12855
- { "hubPath": "${path40.relative(process.cwd(), absPath)}" }
12877
+ { "hubPath": "${path41.relative(process.cwd(), absPath)}" }
12856
12878
  2. Run \`haive hub push\` to publish your shared memories
12857
12879
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
12858
12880
  `
@@ -12881,14 +12903,14 @@ Next steps:
12881
12903
  process.exitCode = 1;
12882
12904
  return;
12883
12905
  }
12884
- const hubRoot = path40.resolve(root, config.hubPath);
12906
+ const hubRoot = path41.resolve(root, config.hubPath);
12885
12907
  if (!existsSync61(hubRoot)) {
12886
12908
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
12887
12909
  process.exitCode = 1;
12888
12910
  return;
12889
12911
  }
12890
- const projectName = path40.basename(root);
12891
- const destDir = path40.join(hubRoot, ".ai", "memories", "shared", projectName);
12912
+ const projectName = path41.basename(root);
12913
+ const destDir = path41.join(hubRoot, ".ai", "memories", "shared", projectName);
12892
12914
  await mkdir17(destDir, { recursive: true });
12893
12915
  const all = await loadMemoriesFromDir30(paths.memoriesDir);
12894
12916
  const shared = all.filter(
@@ -12907,7 +12929,7 @@ Next steps:
12907
12929
  for (const { memory: memory2 } of shared) {
12908
12930
  const fm = memory2.frontmatter;
12909
12931
  const fileName = `${fm.id}.md`;
12910
- const destPath = path40.join(destDir, fileName);
12932
+ const destPath = path41.join(destDir, fileName);
12911
12933
  await writeFile29(destPath, serializeMemory26(memory2), "utf8");
12912
12934
  pushed++;
12913
12935
  }
@@ -12915,7 +12937,7 @@ Next steps:
12915
12937
  console.log(ui.dim(` Location: ${destDir}`));
12916
12938
  if (opts.commit) {
12917
12939
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
12918
- spawnSync5("git", ["add", path40.join(".ai", "memories", "shared", projectName)], {
12940
+ spawnSync5("git", ["add", path41.join(".ai", "memories", "shared", projectName)], {
12919
12941
  cwd: hubRoot
12920
12942
  });
12921
12943
  const commit = spawnSync5("git", ["commit", "-m", message], {
@@ -12950,13 +12972,13 @@ Next steps:
12950
12972
  process.exitCode = 1;
12951
12973
  return;
12952
12974
  }
12953
- const hubRoot = path40.resolve(root, config.hubPath);
12954
- const hubSharedDir = path40.join(hubRoot, ".ai", "memories", "shared");
12975
+ const hubRoot = path41.resolve(root, config.hubPath);
12976
+ const hubSharedDir = path41.join(hubRoot, ".ai", "memories", "shared");
12955
12977
  if (!existsSync61(hubSharedDir)) {
12956
12978
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
12957
12979
  return;
12958
12980
  }
12959
- const projectName = path40.basename(root);
12981
+ const projectName = path41.basename(root);
12960
12982
  const { readdir: readdir7 } = await import("fs/promises");
12961
12983
  const projectDirs = (await readdir7(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
12962
12984
  if (projectDirs.length === 0) {
@@ -12966,16 +12988,16 @@ Next steps:
12966
12988
  let totalImported = 0;
12967
12989
  let totalUpdated = 0;
12968
12990
  for (const sourceName of projectDirs) {
12969
- const sourceDir = path40.join(hubSharedDir, sourceName);
12970
- const destDir = path40.join(paths.memoriesDir, "shared", sourceName);
12991
+ const sourceDir = path41.join(hubSharedDir, sourceName);
12992
+ const destDir = path41.join(paths.memoriesDir, "shared", sourceName);
12971
12993
  await mkdir17(destDir, { recursive: true });
12972
12994
  const sourceFiles = (await readdir7(sourceDir)).filter((f) => f.endsWith(".md"));
12973
12995
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
12974
12996
  const existingInDest = await loadDir(destDir);
12975
12997
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
12976
12998
  for (const file of sourceFiles) {
12977
- const srcPath = path40.join(sourceDir, file);
12978
- const destPath = path40.join(destDir, file);
12999
+ const srcPath = path41.join(sourceDir, file);
13000
+ const destPath = path41.join(destDir, file);
12979
13001
  const fileContent = await readFile19(srcPath, "utf8");
12980
13002
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
12981
13003
  if (!alreadyTagged) {
@@ -13006,14 +13028,14 @@ Next steps:
13006
13028
  console.log(
13007
13029
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
13008
13030
  );
13009
- const sharedDir = path40.join(paths.memoriesDir, "shared");
13031
+ const sharedDir = path41.join(paths.memoriesDir, "shared");
13010
13032
  if (existsSync61(sharedDir)) {
13011
13033
  const { readdir: readdir7 } = await import("fs/promises");
13012
13034
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
13013
13035
  console.log(`
13014
13036
  Imported from ${sources.length} source(s):`);
13015
13037
  for (const src of sources) {
13016
- const files = (await readdir7(path40.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
13038
+ const files = (await readdir7(path41.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
13017
13039
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
13018
13040
  }
13019
13041
  } else {
@@ -13038,7 +13060,7 @@ Next steps:
13038
13060
  import "commander";
13039
13061
  import { existsSync as existsSync63 } from "fs";
13040
13062
  import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
13041
- import path41 from "path";
13063
+ import path43 from "path";
13042
13064
  import {
13043
13065
  aggregateUsage,
13044
13066
  findProjectRoot as findProjectRoot39,
@@ -13110,7 +13132,7 @@ function registerStats(program2) {
13110
13132
  });
13111
13133
  }
13112
13134
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13113
- const outAbs = path41.isAbsolute(outRelative) ? path41.resolve(outRelative) : path41.resolve(root, outRelative);
13135
+ const outAbs = path43.isAbsolute(outRelative) ? path43.resolve(outRelative) : path43.resolve(root, outRelative);
13114
13136
  const size = await usageLogSize(paths);
13115
13137
  let events = await readUsageEvents2(paths);
13116
13138
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
@@ -13145,7 +13167,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
13145
13167
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
13146
13168
  events = [];
13147
13169
  }
13148
- await mkdir18(path41.dirname(outAbs), { recursive: true });
13170
+ await mkdir18(path43.dirname(outAbs), { recursive: true });
13149
13171
  const payload = {
13150
13172
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
13151
13173
  project_root: root,
@@ -13336,7 +13358,7 @@ function summarize(name, t0, payload, notes) {
13336
13358
  // src/commands/benchmark.ts
13337
13359
  import { existsSync as existsSync64 } from "fs";
13338
13360
  import { readdir as readdir5, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
13339
- import path43 from "path";
13361
+ import path44 from "path";
13340
13362
  import "commander";
13341
13363
  import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hiveai/core";
13342
13364
  function registerBenchmark(program2) {
@@ -13351,9 +13373,9 @@ function registerBenchmark(program2) {
13351
13373
  }
13352
13374
  const markdown = renderMarkdown(root, summary, rows);
13353
13375
  if (opts.out) {
13354
- const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
13376
+ const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
13355
13377
  await writeFile31(outFile, markdown, "utf8");
13356
- ui.success(`wrote ${path43.relative(process.cwd(), outFile)}`);
13378
+ ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
13357
13379
  return;
13358
13380
  }
13359
13381
  console.log(markdown);
@@ -13377,9 +13399,9 @@ function registerBenchmark(program2) {
13377
13399
  }
13378
13400
  function resolveBenchmarkRoot(dir) {
13379
13401
  const candidate = dir ?? "benchmarks/agent-benchmark";
13380
- if (path43.isAbsolute(candidate)) return candidate;
13402
+ if (path44.isAbsolute(candidate)) return candidate;
13381
13403
  const projectRoot = findProjectRoot41(process.cwd());
13382
- return path43.join(projectRoot, candidate);
13404
+ return path44.join(projectRoot, candidate);
13383
13405
  }
13384
13406
  async function collectRows(root) {
13385
13407
  if (!existsSync64(root)) throw new Error(`Benchmark directory not found: ${root}`);
@@ -13387,8 +13409,8 @@ async function collectRows(root) {
13387
13409
  const rows = [];
13388
13410
  for (const entry of entries) {
13389
13411
  if (!entry.isDirectory()) continue;
13390
- const fixtureDir = path43.join(root, entry.name);
13391
- const reportFile = path43.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
13412
+ const fixtureDir = path44.join(root, entry.name);
13413
+ const reportFile = path44.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
13392
13414
  if (!existsSync64(reportFile)) continue;
13393
13415
  const report = await readFile20(reportFile, "utf8");
13394
13416
  rows.push(parseAgentReport(entry.name, report));
@@ -13406,7 +13428,7 @@ function parseAgentReport(fixture, report) {
13406
13428
  test_iterations: countMatches(section(report, "Test Iterations"), /Iteration\s+\d+|^- /gim),
13407
13429
  terminal_failures: countMatches(section(report, "Terminal Errors"), /fail|error|not raised|exited with code 1/gi),
13408
13430
  decision_mentions: sectionBulletCount(report, "Key Decisions"),
13409
- token_proxy: estimateTokens4(report),
13431
+ report_tokens_est: estimateTokens4(report),
13410
13432
  haive_impact: /hAIve Memory Impact[\s\S]*?\b(yes|directly|changed|shaped|confirmed)\b/i.test(report)
13411
13433
  };
13412
13434
  }
@@ -13428,7 +13450,7 @@ function summarizeGroup(rows) {
13428
13450
  test_iterations: sum("test_iterations"),
13429
13451
  terminal_failures: sum("terminal_failures"),
13430
13452
  decision_mentions: sum("decision_mentions"),
13431
- token_proxy: sum("token_proxy"),
13453
+ report_tokens_est: sum("report_tokens_est"),
13432
13454
  haive_impact_count: rows.filter((r) => r.haive_impact).length
13433
13455
  };
13434
13456
  }
@@ -13440,29 +13462,31 @@ function renderMarkdown(root, summary, rows) {
13440
13462
  "",
13441
13463
  "## Summary",
13442
13464
  "",
13443
- "| Group | Fixtures | Commands | Files read | Files modified | Test iterations | Terminal failures | Decision mentions | Token proxy | hAIve impact |",
13465
+ "| Group | Fixtures | Commands | Files read | Files modified | Test iterations | Terminal failures | Decision mentions | Report tokens (est, report only) | hAIve impact |",
13444
13466
  "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
13445
13467
  groupLine("hAIve", summary.haive),
13446
13468
  groupLine("Plain", summary.plain),
13447
13469
  "",
13448
13470
  "## Fixtures",
13449
13471
  "",
13450
- "| Fixture | Group | Commands | Files read | Files modified | Test iterations | Terminal failures | Decisions | Token proxy | hAIve impact |",
13472
+ "| Fixture | Group | Commands | Files read | Files modified | Test iterations | Terminal failures | Decisions | Report tokens (est, report only) | hAIve impact |",
13451
13473
  "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |",
13452
13474
  ...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"} |`
13475
+ (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
13476
  ),
13455
13477
  "",
13456
13478
  "## Reading",
13457
13479
  "",
13458
- "The token proxy is estimated from the agent report size, not from private model billing data.",
13480
+ "`Report tokens (est)` estimates the size of the agent's WRITTEN REPORT only \u2014 a verbosity proxy, NOT",
13481
+ "the agent's total token consumption. For real per-agent token/latency, capture your runner's telemetry",
13482
+ "(e.g. subagent token counts) separately; this report can't see model billing.",
13459
13483
  "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
13484
  ""
13461
13485
  ];
13462
13486
  return lines.join("\n");
13463
13487
  }
13464
13488
  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} |`;
13489
+ 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
13490
  }
13467
13491
  function sectionBulletCount(markdown, title) {
13468
13492
  return countMatches(section(markdown, title), /^- |^\d+\.\s/gm);
@@ -13481,7 +13505,7 @@ function escapeRegExp(value) {
13481
13505
  // src/commands/eval.ts
13482
13506
  import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
13483
13507
  import { existsSync as existsSync65 } from "fs";
13484
- import path44 from "path";
13508
+ import path45 from "path";
13485
13509
  import "commander";
13486
13510
  import {
13487
13511
  aggregateRetrieval,
@@ -13577,7 +13601,7 @@ function registerEval(program2) {
13577
13601
  });
13578
13602
  if (!opts.json) ui.success(`Recorded eval score ${report.score}/100 to history.`);
13579
13603
  }
13580
- const baselineFile = opts.baselineFile ? path44.isAbsolute(opts.baselineFile) ? opts.baselineFile : path44.join(root, opts.baselineFile) : path44.join(root, ".ai", "eval", "baseline.json");
13604
+ const baselineFile = opts.baselineFile ? path45.isAbsolute(opts.baselineFile) ? opts.baselineFile : path45.join(root, opts.baselineFile) : path45.join(root, ".ai", "eval", "baseline.json");
13581
13605
  if (opts.baseline) {
13582
13606
  const snapshot = {
13583
13607
  saved_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -13586,18 +13610,18 @@ function registerEval(program2) {
13586
13610
  report,
13587
13611
  gate_precision: gatePrecision
13588
13612
  };
13589
- await mkdir19(path44.dirname(baselineFile), { recursive: true });
13613
+ await mkdir19(path45.dirname(baselineFile), { recursive: true });
13590
13614
  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)}`);
13615
+ if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path45.relative(root, baselineFile)}`);
13592
13616
  }
13593
13617
  let delta = null;
13594
13618
  let gateDelta = null;
13595
13619
  if (opts.compare || opts.regressionGate) {
13596
13620
  if (!existsSync65(baselineFile)) {
13597
13621
  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.`);
13622
+ if (!opts.json) ui.info(`No baseline at ${path45.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
13599
13623
  } else {
13600
- ui.error(`No baseline at ${path44.relative(root, baselineFile)}. Run \`haive eval --baseline\` first.`);
13624
+ ui.error(`No baseline at ${path45.relative(root, baselineFile)}. Run \`haive eval --baseline\` first.`);
13601
13625
  process.exitCode = 1;
13602
13626
  return;
13603
13627
  }
@@ -13636,9 +13660,9 @@ function registerEval(program2) {
13636
13660
  }
13637
13661
  const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision);
13638
13662
  if (opts.out) {
13639
- const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
13663
+ const outFile = path45.isAbsolute(opts.out) ? opts.out : path45.join(root, opts.out);
13640
13664
  await writeFile33(outFile, md, "utf8");
13641
- ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
13665
+ ui.success(`wrote ${path45.relative(process.cwd(), outFile)}`);
13642
13666
  } else {
13643
13667
  console.log(md);
13644
13668
  }
@@ -13722,12 +13746,12 @@ function countCases(spec) {
13722
13746
  }
13723
13747
  async function resolveSpec(opts, root, memoriesDir) {
13724
13748
  if (opts.spec) {
13725
- const file = path44.resolve(opts.spec);
13749
+ const file = path45.resolve(opts.spec);
13726
13750
  const raw = await readFile21(file, "utf8");
13727
13751
  const spec = JSON.parse(raw);
13728
13752
  return { spec, source: file, synthesized: 0, authored: countCases(spec) };
13729
13753
  }
13730
- const defaultSpec = path44.join(root, ".ai", "eval", "spec.json");
13754
+ const defaultSpec = path45.join(root, ".ai", "eval", "spec.json");
13731
13755
  if (existsSync65(defaultSpec)) {
13732
13756
  const raw = await readFile21(defaultSpec, "utf8");
13733
13757
  const explicit = JSON.parse(raw);
@@ -13848,7 +13872,7 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision) {
13848
13872
  // src/commands/memory-suggest.ts
13849
13873
  import { mkdir as mkdir20, writeFile as writeFile34 } from "fs/promises";
13850
13874
  import { existsSync as existsSync66 } from "fs";
13851
- import path45 from "path";
13875
+ import path46 from "path";
13852
13876
  import "commander";
13853
13877
  import {
13854
13878
  aggregateUsage as aggregateUsage2,
@@ -13949,13 +13973,13 @@ function registerMemorySuggest(memory2) {
13949
13973
  });
13950
13974
  const body = renderTemplate(s, fm.id, status);
13951
13975
  const file = memoryFilePath11(paths, fm.scope, fm.id, fm.module);
13952
- await mkdir20(path45.dirname(file), { recursive: true });
13976
+ await mkdir20(path46.dirname(file), { recursive: true });
13953
13977
  if (existsSync66(file)) {
13954
- skipped.push({ query: s.query, reason: `file already exists at ${path45.relative(root, file)}` });
13978
+ skipped.push({ query: s.query, reason: `file already exists at ${path46.relative(root, file)}` });
13955
13979
  continue;
13956
13980
  }
13957
13981
  await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
13958
- created.push({ id: fm.id, file: path45.relative(root, file), query: s.query });
13982
+ created.push({ id: fm.id, file: path46.relative(root, file), query: s.query });
13959
13983
  }
13960
13984
  if (opts.json) {
13961
13985
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -14055,7 +14079,7 @@ function truncate2(text, max) {
14055
14079
  // src/commands/memory-archive.ts
14056
14080
  import { existsSync as existsSync67 } from "fs";
14057
14081
  import { writeFile as writeFile35 } from "fs/promises";
14058
- import path46 from "path";
14082
+ import path47 from "path";
14059
14083
  import "commander";
14060
14084
  import {
14061
14085
  findProjectRoot as findProjectRoot44,
@@ -14099,7 +14123,7 @@ function registerMemoryArchive(memory2) {
14099
14123
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
14100
14124
  const retired = retirementSignal2(fm, mem.body);
14101
14125
  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)));
14126
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync67(path47.join(paths.root, p)));
14103
14127
  const isAnchorless = !hasAnyAnchor;
14104
14128
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
14105
14129
  const u = getUsage21(usage, fm.id);
@@ -14176,7 +14200,7 @@ function parseDays(input) {
14176
14200
  // src/commands/doctor.ts
14177
14201
  import { existsSync as existsSync68, statSync as statSync2 } from "fs";
14178
14202
  import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
14179
- import path47 from "path";
14203
+ import path48 from "path";
14180
14204
  import { execFileSync, execSync as execSync3 } from "child_process";
14181
14205
  import "commander";
14182
14206
  import {
@@ -14401,7 +14425,7 @@ function registerDoctor(program2) {
14401
14425
  }
14402
14426
  }
14403
14427
  if (config.enforcement?.requireBriefingFirst) {
14404
- const claudeSettings = path47.join(root, ".claude", "settings.local.json");
14428
+ const claudeSettings = path48.join(root, ".claude", "settings.local.json");
14405
14429
  let hasClaudeEnforcement = false;
14406
14430
  if (existsSync68(claudeSettings)) {
14407
14431
  try {
@@ -14429,7 +14453,7 @@ function registerDoctor(program2) {
14429
14453
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
14430
14454
  });
14431
14455
  }
14432
- findings.push(...await collectInstallFindings(root, "0.23.0"));
14456
+ findings.push(...await collectInstallFindings(root, "0.24.0"));
14433
14457
  findings.push(...await collectToolchainFindings(root));
14434
14458
  try {
14435
14459
  const legacyRaw = execSync3("haive-mcp --version", {
@@ -14437,7 +14461,7 @@ function registerDoctor(program2) {
14437
14461
  timeout: 3e3,
14438
14462
  stdio: ["ignore", "pipe", "ignore"]
14439
14463
  }).trim();
14440
- const cliVersion = "0.23.0";
14464
+ const cliVersion = "0.24.0";
14441
14465
  if (legacyRaw && legacyRaw !== cliVersion) {
14442
14466
  findings.push({
14443
14467
  severity: "warn",
@@ -14453,9 +14477,9 @@ npm uninstall -g @hiveai/mcp`
14453
14477
  }
14454
14478
  {
14455
14479
  const configPaths = [
14456
- path47.join(root, ".mcp.json"),
14457
- path47.join(root, ".cursor", "mcp.json"),
14458
- path47.join(root, ".vscode", "mcp.json")
14480
+ path48.join(root, ".mcp.json"),
14481
+ path48.join(root, ".cursor", "mcp.json"),
14482
+ path48.join(root, ".vscode", "mcp.json")
14459
14483
  ];
14460
14484
  const staleConfigs = [];
14461
14485
  for (const cfgPath of configPaths) {
@@ -14463,7 +14487,7 @@ npm uninstall -g @hiveai/mcp`
14463
14487
  try {
14464
14488
  const raw = await readFile23(cfgPath, "utf8");
14465
14489
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
14466
- staleConfigs.push(path47.relative(root, cfgPath));
14490
+ staleConfigs.push(path48.relative(root, cfgPath));
14467
14491
  if (opts.fix && !opts.dryRun) {
14468
14492
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
14469
14493
  await writeFile36(cfgPath, updated, "utf8");
@@ -14754,7 +14778,7 @@ which -a haive`
14754
14778
  ".vscode/mcp.json"
14755
14779
  ];
14756
14780
  for (const rel of integrationFiles) {
14757
- const file = path47.join(root, rel);
14781
+ const file = path48.join(root, rel);
14758
14782
  if (!existsSync68(file)) continue;
14759
14783
  const text = await readFile23(file, "utf8").catch(() => "");
14760
14784
  for (const bin of extractAbsoluteHaiveBins(text)) {
@@ -14781,7 +14805,7 @@ which -a haive`
14781
14805
  async function collectToolchainFindings(root) {
14782
14806
  const findings = [];
14783
14807
  const pkg = await readJson(
14784
- path47.join(root, "package.json")
14808
+ path48.join(root, "package.json")
14785
14809
  );
14786
14810
  const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
14787
14811
  if (!wantsPnpm) return findings;
@@ -14800,9 +14824,9 @@ async function collectToolchainFindings(root) {
14800
14824
  }
14801
14825
  async function collectDistFreshnessFindings(root, expectedVersion) {
14802
14826
  const findings = [];
14803
- const isHaiveWorkspace = (await readJson(path47.join(root, "package.json")))?.name === "haive-monorepo";
14827
+ const isHaiveWorkspace = (await readJson(path48.join(root, "package.json")))?.name === "haive-monorepo";
14804
14828
  if (!isHaiveWorkspace) return findings;
14805
- const cliDist = path47.join(root, "packages/cli/dist/index.js");
14829
+ const cliDist = path48.join(root, "packages/cli/dist/index.js");
14806
14830
  if (!existsSync68(cliDist)) {
14807
14831
  findings.push({
14808
14832
  severity: "warn",
@@ -14827,7 +14851,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14827
14851
  "packages/core/src/index.ts",
14828
14852
  "packages/mcp/src/server.ts",
14829
14853
  "packages/cli/src/index.ts"
14830
- ].map((rel) => path47.join(root, rel)).filter(existsSync68);
14854
+ ].map((rel) => path48.join(root, rel)).filter(existsSync68);
14831
14855
  if (sourceFiles.length > 0) {
14832
14856
  const distMtime = statSync2(cliDist).mtimeMs;
14833
14857
  const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
@@ -14845,7 +14869,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
14845
14869
  }
14846
14870
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
14847
14871
  const findings = [];
14848
- const rootPkg = await readJson(path47.join(root, "package.json"));
14872
+ const rootPkg = await readJson(path48.join(root, "package.json"));
14849
14873
  const workspacePackages = [
14850
14874
  "packages/core/package.json",
14851
14875
  "packages/embeddings/package.json",
@@ -14854,7 +14878,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
14854
14878
  ];
14855
14879
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
14856
14880
  rel,
14857
- pkg: await readJson(path47.join(root, rel))
14881
+ pkg: await readJson(path48.join(root, rel))
14858
14882
  })))).filter((item) => item.pkg);
14859
14883
  const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
14860
14884
  if (!isHaiveWorkspace) return findings;
@@ -15359,21 +15383,21 @@ function registerMemorySuggestTopic(memory2) {
15359
15383
  }
15360
15384
 
15361
15385
  // src/commands/resolve-project.ts
15362
- import path48 from "path";
15386
+ import path49 from "path";
15363
15387
  import "commander";
15364
15388
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
15365
15389
  function registerResolveProject(program2) {
15366
15390
  program2.command("resolve-project").description(
15367
15391
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
15368
15392
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
15369
- const info = resolveProjectInfo2({ cwd: path48.resolve(opts.dir) });
15393
+ const info = resolveProjectInfo2({ cwd: path49.resolve(opts.dir) });
15370
15394
  console.log(JSON.stringify({ ok: true, info }, null, 2));
15371
15395
  });
15372
15396
  }
15373
15397
 
15374
15398
  // src/commands/runtime-journal.ts
15375
15399
  import { existsSync as existsSync71 } from "fs";
15376
- import path49 from "path";
15400
+ import path50 from "path";
15377
15401
  import "commander";
15378
15402
  import {
15379
15403
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
@@ -15387,15 +15411,15 @@ function registerRuntime(program2) {
15387
15411
  );
15388
15412
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
15389
15413
  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());
15414
+ const root = path50.resolve(opts.dir ?? process.cwd());
15391
15415
  const paths = resolveHaivePaths45(findProjectRoot49(root));
15392
15416
  const raw = opts.kind ?? "note";
15393
15417
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
15394
15418
  await appendRuntimeJournalEntry3(paths, { kind, message });
15395
- ui.success(`Appended to ${path49.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
15419
+ ui.success(`Appended to ${path50.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
15396
15420
  });
15397
15421
  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());
15422
+ const root = path50.resolve(opts.dir ?? process.cwd());
15399
15423
  const paths = resolveHaivePaths45(findProjectRoot49(root));
15400
15424
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
15401
15425
  if (!existsSync71(paths.haiveDir)) {
@@ -15414,7 +15438,7 @@ function registerRuntime(program2) {
15414
15438
 
15415
15439
  // src/commands/memory-timeline.ts
15416
15440
  import { existsSync as existsSync73 } from "fs";
15417
- import path50 from "path";
15441
+ import path51 from "path";
15418
15442
  import "commander";
15419
15443
  import {
15420
15444
  collectTimelineEntries as collectTimelineEntries2,
@@ -15430,7 +15454,7 @@ function registerMemoryTimeline(memory2) {
15430
15454
  process.exitCode = 1;
15431
15455
  return;
15432
15456
  }
15433
- const root = path50.resolve(opts.dir ?? process.cwd());
15457
+ const root = path51.resolve(opts.dir ?? process.cwd());
15434
15458
  const paths = resolveHaivePaths46(findProjectRoot50(root));
15435
15459
  if (!existsSync73(paths.memoriesDir)) {
15436
15460
  ui.error("No memories \u2014 run `haive init`.");
@@ -15451,7 +15475,7 @@ function registerMemoryTimeline(memory2) {
15451
15475
 
15452
15476
  // src/commands/memory-conflict-candidates.ts
15453
15477
  import { existsSync as existsSync74 } from "fs";
15454
- import path51 from "path";
15478
+ import path53 from "path";
15455
15479
  import "commander";
15456
15480
  import {
15457
15481
  findLexicalConflictPairs as findLexicalConflictPairs2,
@@ -15473,7 +15497,7 @@ function registerMemoryConflictCandidates(memory2) {
15473
15497
  "decision,architecture,convention,gotcha (lexical scan)",
15474
15498
  "decision,architecture"
15475
15499
  ).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());
15500
+ const root = path53.resolve(opts.dir ?? process.cwd());
15477
15501
  const paths = resolveHaivePaths47(findProjectRoot51(root));
15478
15502
  if (!existsSync74(paths.memoriesDir)) {
15479
15503
  ui.error("No memories \u2014 run `haive init`.");
@@ -15513,7 +15537,7 @@ function registerMemoryConflictCandidates(memory2) {
15513
15537
  import { execFile as execFile3, execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
15514
15538
  import { existsSync as existsSync75, statSync as statSync3 } from "fs";
15515
15539
  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";
15540
+ import path54 from "path";
15517
15541
  import { promisify as promisify3 } from "util";
15518
15542
  import "commander";
15519
15543
  import {
@@ -15573,7 +15597,7 @@ function registerEnforce(program2) {
15573
15597
  if (opts.claude !== false) {
15574
15598
  try {
15575
15599
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
15576
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path53.relative(root, result.settingsPath)})`);
15600
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path54.relative(root, result.settingsPath)})`);
15577
15601
  } catch (err) {
15578
15602
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
15579
15603
  }
@@ -15594,19 +15618,19 @@ function registerEnforce(program2) {
15594
15618
  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
15619
  const root = findProjectRoot52(opts.dir);
15596
15620
  const paths = resolveHaivePaths48(root);
15597
- const cacheDir = path53.join(paths.haiveDir, ".cache");
15621
+ const cacheDir = path54.join(paths.haiveDir, ".cache");
15598
15622
  if (existsSync75(cacheDir)) {
15599
- if (opts.dryRun) ui.info(`would clean ${path53.relative(root, cacheDir)} (preserving .gitignore)`);
15623
+ if (opts.dryRun) ui.info(`would clean ${path54.relative(root, cacheDir)} (preserving .gitignore)`);
15600
15624
  else {
15601
15625
  const removed = await cleanupCacheDir(cacheDir);
15602
- ui.success(`cleaned ${path53.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15626
+ ui.success(`cleaned ${path54.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15603
15627
  }
15604
15628
  }
15605
15629
  if (existsSync75(paths.runtimeDir)) {
15606
- if (opts.dryRun) ui.info(`would clean ${path53.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
15630
+ if (opts.dryRun) ui.info(`would clean ${path54.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
15607
15631
  else {
15608
15632
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
15609
- ui.success(`cleaned ${path53.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15633
+ ui.success(`cleaned ${path54.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
15610
15634
  }
15611
15635
  }
15612
15636
  });
@@ -15959,7 +15983,7 @@ async function buildFinishReport(dir) {
15959
15983
  async function checkFailureCapture(paths, config) {
15960
15984
  const gate = config.enforcement?.failureCaptureGate ?? "warn";
15961
15985
  if (gate === "off") return [];
15962
- const obsFile = path53.join(paths.haiveDir, ".cache", "observations.jsonl");
15986
+ const obsFile = path54.join(paths.haiveDir, ".cache", "observations.jsonl");
15963
15987
  if (!existsSync75(obsFile)) return [];
15964
15988
  const failures = [];
15965
15989
  try {
@@ -16031,7 +16055,7 @@ async function runWithEnforcement(command, args, opts) {
16031
16055
  process.exit(2);
16032
16056
  }
16033
16057
  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`);
16058
+ ui.info(`Briefing written to ${path54.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
16035
16059
  const child = spawn6(command, args, {
16036
16060
  cwd: root,
16037
16061
  stdio: "inherit",
@@ -16081,9 +16105,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
16081
16105
  source: "haive-run",
16082
16106
  memoryIds: briefing.memories.map((m) => m.id)
16083
16107
  });
16084
- const dir = path53.join(paths.runtimeDir, "enforcement", "briefings");
16108
+ const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
16085
16109
  await mkdir21(dir, { recursive: true });
16086
- const file = path53.join(dir, `${sessionId}.md`);
16110
+ const file = path54.join(dir, `${sessionId}.md`);
16087
16111
  const parts = [
16088
16112
  "# hAIve Briefing",
16089
16113
  "",
@@ -16141,7 +16165,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
16141
16165
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
16142
16166
  });
16143
16167
  }
16144
- findings.push(...await inspectIntegrationVersions(root, "0.23.0"));
16168
+ findings.push(...await inspectIntegrationVersions(root, "0.24.0"));
16145
16169
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
16146
16170
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
16147
16171
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -16292,7 +16316,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
16292
16316
  const consulted = new Set(marker?.memory_ids ?? []);
16293
16317
  const missing = relevant.filter(({ memory: memory2, filePath }) => {
16294
16318
  if (consulted.has(memory2.frontmatter.id)) return false;
16295
- if (changedSet.has(path53.relative(paths.root, filePath))) return false;
16319
+ if (changedSet.has(path54.relative(paths.root, filePath))) return false;
16296
16320
  return true;
16297
16321
  }).map(({ memory: memory2 }) => memory2);
16298
16322
  if (missing.length === 0) {
@@ -16500,16 +16524,16 @@ async function cleanupRuntimeDir(runtimeDir) {
16500
16524
  for (const entry of entries) {
16501
16525
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
16502
16526
  if (entry.name === "enforcement") {
16503
- removed += await cleanupEnforcementDir(path53.join(runtimeDir, entry.name));
16527
+ removed += await cleanupEnforcementDir(path54.join(runtimeDir, entry.name));
16504
16528
  continue;
16505
16529
  }
16506
- await rm3(path53.join(runtimeDir, entry.name), { recursive: true, force: true });
16530
+ await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
16507
16531
  removed++;
16508
16532
  }
16509
- await writeFile37(path53.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16510
- if (!existsSync75(path53.join(runtimeDir, "README.md"))) {
16533
+ await writeFile37(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
16534
+ if (!existsSync75(path54.join(runtimeDir, "README.md"))) {
16511
16535
  await writeFile37(
16512
- path53.join(runtimeDir, "README.md"),
16536
+ path54.join(runtimeDir, "README.md"),
16513
16537
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
16514
16538
  "utf8"
16515
16539
  );
@@ -16522,10 +16546,10 @@ async function cleanupCacheDir(cacheDir) {
16522
16546
  const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
16523
16547
  for (const entry of entries) {
16524
16548
  if (entry.name === ".gitignore") continue;
16525
- await rm3(path53.join(cacheDir, entry.name), { recursive: true, force: true });
16549
+ await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
16526
16550
  removed++;
16527
16551
  }
16528
- await writeFile37(path53.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16552
+ await writeFile37(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
16529
16553
  return removed;
16530
16554
  }
16531
16555
  async function cleanupEnforcementDir(enforcementDir) {
@@ -16533,7 +16557,7 @@ async function cleanupEnforcementDir(enforcementDir) {
16533
16557
  const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
16534
16558
  for (const entry of entries) {
16535
16559
  if (entry.name === "briefings") continue;
16536
- await rm3(path53.join(enforcementDir, entry.name), { recursive: true, force: true });
16560
+ await rm3(path54.join(enforcementDir, entry.name), { recursive: true, force: true });
16537
16561
  removed++;
16538
16562
  }
16539
16563
  return removed;
@@ -16549,7 +16573,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
16549
16573
  ];
16550
16574
  const findings = [];
16551
16575
  for (const rel of files) {
16552
- const file = path53.join(root, rel);
16576
+ const file = path54.join(root, rel);
16553
16577
  if (!existsSync75(file)) continue;
16554
16578
  const text = await readFile24(file, "utf8").catch(() => "");
16555
16579
  for (const bin of extractAbsoluteHaiveBins2(text)) {
@@ -16765,7 +16789,7 @@ function isShippablePath(file) {
16765
16789
  }
16766
16790
  var CI_SKIP_DIRECTIVE = /\[skip ci\]|\[ci skip\]|\[no ci\]|\[skip actions\]|\*\*\*NO_CI\*\*\*|skip-checks: *true/i;
16767
16791
  async function checkCommitMessageSkipCi(root, msgfile) {
16768
- const file = path53.isAbsolute(msgfile) ? msgfile : path53.join(root, msgfile);
16792
+ const file = path54.isAbsolute(msgfile) ? msgfile : path54.join(root, msgfile);
16769
16793
  const raw = await readFile24(file, "utf8").catch(() => "");
16770
16794
  const cleaned = raw.split("\n").filter((line) => !line.startsWith("#")).join("\n");
16771
16795
  if (!CI_SKIP_DIRECTIVE.test(cleaned)) return { block: false, message: "" };
@@ -16794,7 +16818,7 @@ async function inspectReleaseVersionState(root, upstream) {
16794
16818
  }
16795
16819
  async function readPackageVersion(root, relPath) {
16796
16820
  try {
16797
- const data = JSON.parse(await readFile24(path53.join(root, relPath), "utf8"));
16821
+ const data = JSON.parse(await readFile24(path54.join(root, relPath), "utf8"));
16798
16822
  return typeof data.version === "string" ? data.version : void 0;
16799
16823
  } catch {
16800
16824
  return void 0;
@@ -16982,8 +17006,8 @@ function buildScore(findings, threshold = 80) {
16982
17006
  };
16983
17007
  }
16984
17008
  async function installGitEnforcement(root) {
16985
- const hooksDir = path53.join(root, ".git", "hooks");
16986
- if (!existsSync75(path53.join(root, ".git"))) {
17009
+ const hooksDir = path54.join(root, ".git", "hooks");
17010
+ if (!existsSync75(path54.join(root, ".git"))) {
16987
17011
  ui.warn("No .git directory found; git enforcement hooks skipped.");
16988
17012
  return;
16989
17013
  }
@@ -17012,7 +17036,7 @@ haive enforce commit-msg "$1" --dir . || exit $?
17012
17036
  }
17013
17037
  ];
17014
17038
  for (const hook of hooks) {
17015
- const file = path53.join(hooksDir, hook.name);
17039
+ const file = path54.join(hooksDir, hook.name);
17016
17040
  if (existsSync75(file)) {
17017
17041
  const current = await readFile24(file, "utf8").catch(() => "");
17018
17042
  if (current.includes(ENFORCE_HOOK_MARKER)) {
@@ -17030,8 +17054,8 @@ ${hook.body}`, "utf8");
17030
17054
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push, commit-msg");
17031
17055
  }
17032
17056
  async function installCiEnforcement(root) {
17033
- const workflowPath = path53.join(root, ".github", "workflows", "haive-enforcement.yml");
17034
- await mkdir21(path53.dirname(workflowPath), { recursive: true });
17057
+ const workflowPath = path54.join(root, ".github", "workflows", "haive-enforcement.yml");
17058
+ await mkdir21(path54.dirname(workflowPath), { recursive: true });
17035
17059
  if (existsSync75(workflowPath)) {
17036
17060
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
17037
17061
  return;
@@ -17063,7 +17087,7 @@ jobs:
17063
17087
  HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
17064
17088
  run: haive enforce ci
17065
17089
  `, "utf8");
17066
- ui.success(`Created ${path53.relative(root, workflowPath)}`);
17090
+ ui.success(`Created ${path54.relative(root, workflowPath)}`);
17067
17091
  }
17068
17092
  function printReport(report, json, explain = false) {
17069
17093
  if (json) {
@@ -17167,8 +17191,8 @@ function extractToolPaths(payload, root) {
17167
17191
  }
17168
17192
  function normalizeToolPath(file, root) {
17169
17193
  const normalized = file.replace(/\\/g, "/");
17170
- if (!path53.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
17171
- return path53.relative(root, normalized).replace(/\\/g, "/");
17194
+ if (!path54.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
17195
+ return path54.relative(root, normalized).replace(/\\/g, "/");
17172
17196
  }
17173
17197
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
17174
17198
  if (!existsSync75(paths.memoriesDir)) return [];
@@ -17216,7 +17240,7 @@ async function readStdin2(maxBytes) {
17216
17240
  }
17217
17241
  var ATOMIC_STAGE_EXCLUDE = ["/.usage/", "/.runtime/", "/.cache/"];
17218
17242
  async function stageResyncedArtifacts(root, paths) {
17219
- const aiRel = path53.relative(root, paths.haiveDir);
17243
+ const aiRel = path54.relative(root, paths.haiveDir);
17220
17244
  const out = await runCommand4("git", ["diff", "--name-only", "--", aiRel], root).catch(() => "");
17221
17245
  const toStage = out.split("\n").map((line) => line.trim()).filter(Boolean).filter((file) => !ATOMIC_STAGE_EXCLUDE.some((excl) => `/${file}`.includes(excl)));
17222
17246
  if (toStage.length === 0) return;
@@ -17258,7 +17282,7 @@ function registerRun(program2) {
17258
17282
  import { execFile as execFile4 } from "child_process";
17259
17283
  import { existsSync as existsSync76 } from "fs";
17260
17284
  import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
17261
- import path54 from "path";
17285
+ import path55 from "path";
17262
17286
  import { promisify as promisify4 } from "util";
17263
17287
  import "commander";
17264
17288
  import {
@@ -17303,7 +17327,7 @@ function registerSensors(program2) {
17303
17327
  const root = findProjectRoot53(opts.dir);
17304
17328
  const paths = resolveHaivePaths49(root);
17305
17329
  const memories = await runnableSensorMemories(paths);
17306
- const diff = opts.diffFile ? await readFile25(path54.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
17330
+ const diff = opts.diffFile ? await readFile25(path55.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
17307
17331
  const targets = sensorTargetsFromDiff3(diff);
17308
17332
  const hits = runSensors3(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
17309
17333
  const config = await loadConfig15(paths);
@@ -17415,13 +17439,13 @@ function registerSensors(program2) {
17415
17439
  const root = findProjectRoot53(opts.dir);
17416
17440
  const paths = resolveHaivePaths49(root);
17417
17441
  const rows = await sensorRows(paths);
17418
- const outDir = path54.resolve(root, opts.outDir ?? ".ai/generated");
17442
+ const outDir = path55.resolve(root, opts.outDir ?? ".ai/generated");
17419
17443
  await mkdir23(outDir, { recursive: true });
17420
- const outPath = path54.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
17444
+ const outPath = path55.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
17421
17445
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
17422
17446
  await writeFile38(outPath, content, "utf8");
17423
17447
  if (format === "grep") await chmod3(outPath, 493);
17424
- ui.success(`Exported ${rows.length} sensor(s): ${path54.relative(root, outPath)}`);
17448
+ ui.success(`Exported ${rows.length} sensor(s): ${path55.relative(root, outPath)}`);
17425
17449
  });
17426
17450
  }
17427
17451
  async function sensorRows(paths) {
@@ -17494,7 +17518,7 @@ function shellQuote(value) {
17494
17518
  // src/commands/ingest.ts
17495
17519
  import { existsSync as existsSync77 } from "fs";
17496
17520
  import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
17497
- import path55 from "path";
17521
+ import path56 from "path";
17498
17522
  import "commander";
17499
17523
  import {
17500
17524
  draftsFromFindings as draftsFromFindings2,
@@ -17551,7 +17575,7 @@ function registerIngest(program2) {
17551
17575
  process.exitCode = 1;
17552
17576
  return;
17553
17577
  }
17554
- const reportPath = path55.resolve(root, file);
17578
+ const reportPath = path56.resolve(root, file);
17555
17579
  if (!existsSync77(reportPath)) {
17556
17580
  ui.error(`Report file not found: ${reportPath}`);
17557
17581
  process.exitCode = 1;
@@ -17640,13 +17664,13 @@ function registerIngest(program2) {
17640
17664
  await writeDraft2(paths, draft);
17641
17665
  created++;
17642
17666
  }
17643
- ui.success(`Created ${created} proposed memory(ies) under ${path55.relative(root, paths.memoriesDir)}/`);
17667
+ ui.success(`Created ${created} proposed memory(ies) under ${path56.relative(root, paths.memoriesDir)}/`);
17644
17668
  ui.info("Review with `haive memory pending`; promote sensors with `haive sensors promote <id> --yes`.");
17645
17669
  });
17646
17670
  }
17647
17671
  async function writeDraft2(paths, draft) {
17648
17672
  const file = memoryFilePath12(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
17649
- await mkdir24(path55.dirname(file), { recursive: true });
17673
+ await mkdir24(path56.dirname(file), { recursive: true });
17650
17674
  await writeFile39(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
17651
17675
  return file;
17652
17676
  }
@@ -17738,6 +17762,18 @@ function renderDashboard(r) {
17738
17762
  console.log(` ${ui.dim("scopes:")} ${formatCounts(inv.by_scope)}`);
17739
17763
  console.log(` ${ui.dim("types: ")} ${formatCounts(inv.by_type)}`);
17740
17764
  console.log();
17765
+ console.log(ui.bold("Value") + ui.dim(" (what hAIve demonstrably earned \u2014 vs its per-task cost)"));
17766
+ const blocked = prevention.trend.last_30d;
17767
+ const demonstrated = impact.high;
17768
+ console.log(
17769
+ ` ${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`
17770
+ );
17771
+ console.log(
17772
+ ui.dim(
17773
+ " Cost is real: the briefing adds context to every task; the payoff is downstream (defects/incidents avoided), not the agent's token bill."
17774
+ )
17775
+ );
17776
+ console.log();
17741
17777
  console.log(ui.bold("Prevention") + ui.dim(" (caught-for-you outcome)"));
17742
17778
  console.log(
17743
17779
  ` ${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 +17861,7 @@ function warnNum(n) {
17825
17861
  import { execFile as execFile5 } from "child_process";
17826
17862
  import { cp, readFile as readFile27 } from "fs/promises";
17827
17863
  import { existsSync as existsSync79 } from "fs";
17828
- import path56 from "path";
17864
+ import path57 from "path";
17829
17865
  import { promisify as promisify5 } from "util";
17830
17866
  import "commander";
17831
17867
  import { findProjectRoot as findProjectRoot56 } from "@hiveai/core";
@@ -17834,7 +17870,7 @@ function registerDevLink(program2) {
17834
17870
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on hAIve itself.");
17835
17871
  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
17872
  const root = findProjectRoot56(opts.dir);
17837
- if (!existsSync79(path56.join(root, "packages", "cli", "dist", "index.js"))) {
17873
+ if (!existsSync79(path57.join(root, "packages", "cli", "dist", "index.js"))) {
17838
17874
  ui.error(`Not the hAIve monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
17839
17875
  process.exitCode = 1;
17840
17876
  return;
@@ -17843,9 +17879,9 @@ function registerDevLink(program2) {
17843
17879
  try {
17844
17880
  globalModules = (await exec3("npm", ["root", "-g"])).stdout.trim();
17845
17881
  } catch {
17846
- globalModules = path56.join(path56.dirname(path56.dirname(process.execPath)), "lib", "node_modules");
17882
+ globalModules = path57.join(path57.dirname(path57.dirname(process.execPath)), "lib", "node_modules");
17847
17883
  }
17848
- const globalHive = path56.join(globalModules, "@hiveai");
17884
+ const globalHive = path57.join(globalModules, "@hiveai");
17849
17885
  if (!existsSync79(globalHive)) {
17850
17886
  ui.error(`No global @hiveai install at ${globalHive}. Install once with \`npm i -g @hiveai/cli\`, then re-run.`);
17851
17887
  process.exitCode = 1;
@@ -17853,21 +17889,21 @@ function registerDevLink(program2) {
17853
17889
  }
17854
17890
  const linked = [];
17855
17891
  const copyDist = async (fromPkg, toDistDir) => {
17856
- const from = path56.join(root, "packages", fromPkg, "dist");
17857
- if (!existsSync79(from) || !existsSync79(path56.dirname(toDistDir))) return;
17892
+ const from = path57.join(root, "packages", fromPkg, "dist");
17893
+ if (!existsSync79(from) || !existsSync79(path57.dirname(toDistDir))) return;
17858
17894
  await cp(from, toDistDir, { recursive: true });
17859
- linked.push(path56.relative(globalModules, toDistDir));
17895
+ linked.push(path57.relative(globalModules, toDistDir));
17860
17896
  };
17861
17897
  for (const pkg of ["cli", "mcp"]) {
17862
- await copyDist(pkg, path56.join(globalHive, pkg, "dist"));
17898
+ await copyDist(pkg, path57.join(globalHive, pkg, "dist"));
17863
17899
  for (const nested of ["core", "embeddings"]) {
17864
- await copyDist(nested, path56.join(globalHive, pkg, "node_modules", "@hiveai", nested, "dist"));
17900
+ await copyDist(nested, path57.join(globalHive, pkg, "node_modules", "@hiveai", nested, "dist"));
17865
17901
  }
17866
17902
  }
17867
- await copyDist("core", path56.join(globalHive, "core", "dist"));
17903
+ await copyDist("core", path57.join(globalHive, "core", "dist"));
17868
17904
  let version = "unknown";
17869
17905
  try {
17870
- version = JSON.parse(await readFile27(path56.join(root, "package.json"), "utf8")).version ?? "unknown";
17906
+ version = JSON.parse(await readFile27(path57.join(root, "package.json"), "utf8")).version ?? "unknown";
17871
17907
  } catch {
17872
17908
  }
17873
17909
  if (opts.json) {
@@ -17887,7 +17923,7 @@ function registerDevLink(program2) {
17887
17923
  // src/commands/coverage.ts
17888
17924
  import { readFile as readFile28 } from "fs/promises";
17889
17925
  import { existsSync as existsSync80 } from "fs";
17890
- import path57 from "path";
17926
+ import path58 from "path";
17891
17927
  import "commander";
17892
17928
  import {
17893
17929
  findCoverageGaps,
@@ -17911,7 +17947,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
17911
17947
  }
17912
17948
  for (const f of obs.files ?? []) {
17913
17949
  if (typeof f !== "string" || !f) continue;
17914
- const rel = path57.isAbsolute(f) ? path57.relative(root, f) : f;
17950
+ const rel = path58.isAbsolute(f) ? path58.relative(root, f) : f;
17915
17951
  if (rel.startsWith("..")) continue;
17916
17952
  files.push(rel);
17917
17953
  }
@@ -17955,7 +17991,7 @@ function registerCoverage(program2) {
17955
17991
  }) : null;
17956
17992
  const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
17957
17993
  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)) : [];
17994
+ const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path58.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
17959
17995
  const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
17960
17996
  const memories = await loadMemoriesFromDir27(paths.memoriesDir);
17961
17997
  const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
@@ -17994,7 +18030,7 @@ function registerCoverage(program2) {
17994
18030
  // src/commands/merge-driver.ts
17995
18031
  import { execFileSync as execFileSync3 } from "child_process";
17996
18032
  import { readFileSync, writeFileSync, existsSync as existsSync81 } from "fs";
17997
- import path58 from "path";
18033
+ import path59 from "path";
17998
18034
  import "commander";
17999
18035
  import { findProjectRoot as findProjectRoot58, mergeMemoryVersions } from "@hiveai/core";
18000
18036
  var GITATTRIBUTES_MARK = "# hAIve merge driver";
@@ -18026,7 +18062,7 @@ function registerMergeDriver(program2) {
18026
18062
  process.exitCode = 1;
18027
18063
  return;
18028
18064
  }
18029
- const gaPath = path58.join(root, ".gitattributes");
18065
+ const gaPath = path59.join(root, ".gitattributes");
18030
18066
  let content = existsSync81(gaPath) ? readFileSync(gaPath, "utf8") : "";
18031
18067
  if (!content.includes(GITATTRIBUTES_MARK)) {
18032
18068
  if (content.length > 0 && !content.endsWith("\n")) content += "\n";
@@ -18113,7 +18149,7 @@ function registerMemoryResolveConflict(memory2) {
18113
18149
  import { execFile as execFile6 } from "child_process";
18114
18150
  import { mkdir as mkdir25, writeFile as writeFile41 } from "fs/promises";
18115
18151
  import { existsSync as existsSync84 } from "fs";
18116
- import path59 from "path";
18152
+ import path60 from "path";
18117
18153
  import { promisify as promisify6 } from "util";
18118
18154
  import "commander";
18119
18155
  import {
@@ -18175,7 +18211,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
18175
18211
  `;
18176
18212
  const file = memoryFilePath13(paths, fm.scope, fm.id, fm.module);
18177
18213
  if (existsSync84(file)) continue;
18178
- await mkdir25(path59.dirname(file), { recursive: true });
18214
+ await mkdir25(path60.dirname(file), { recursive: true });
18179
18215
  await writeFile41(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
18180
18216
  written += 1;
18181
18217
  }
@@ -18208,7 +18244,7 @@ async function readCommits(root, days) {
18208
18244
 
18209
18245
  // src/commands/bridges.ts
18210
18246
  import { existsSync as existsSync85 } from "fs";
18211
- import path60 from "path";
18247
+ import path61 from "path";
18212
18248
  import "commander";
18213
18249
  import {
18214
18250
  findProjectRoot as findProjectRoot61,
@@ -18248,7 +18284,7 @@ function registerBridges(program2) {
18248
18284
  targets = BRIDGE_TARGETS3;
18249
18285
  } else {
18250
18286
  targets = BRIDGE_TARGETS3.filter(
18251
- (t) => existsSync85(path60.join(root, BRIDGE_TARGET_PATH3[t]))
18287
+ (t) => existsSync85(path61.join(root, BRIDGE_TARGET_PATH3[t]))
18252
18288
  );
18253
18289
  if (targets.length === 0) {
18254
18290
  ui.info(
@@ -18277,7 +18313,7 @@ function registerBridges(program2) {
18277
18313
  console.log(ui.bold("hAIve bridge targets:"));
18278
18314
  for (const target of BRIDGE_TARGETS3) {
18279
18315
  const relPath = BRIDGE_TARGET_PATH3[target];
18280
- const exists = existsSync85(path60.join(root, relPath));
18316
+ const exists = existsSync85(path61.join(root, relPath));
18281
18317
  const marker = exists ? ui.dim("\u2713") : ui.dim("\xB7");
18282
18318
  console.log(` ${marker} ${target.padEnd(10)} ${relPath}${exists ? "" : " (not present)"}`);
18283
18319
  }
@@ -18288,7 +18324,7 @@ function registerBridges(program2) {
18288
18324
 
18289
18325
  // src/index.ts
18290
18326
  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");
18327
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.24.0").option("--advanced", "show maintenance and experimental commands in help");
18292
18328
  registerInit(program);
18293
18329
  registerWelcome(program);
18294
18330
  registerResolveProject(program);