@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 +371 -335
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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
|
|
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", "--
|
|
98
|
-
|
|
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(([
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 ${
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
1543
|
+
return path8.join(HOME, ".cursor", "mcp.json");
|
|
1540
1544
|
}
|
|
1541
1545
|
async function configureCursor() {
|
|
1542
1546
|
const mcpPath = cursorMcpPath();
|
|
1543
|
-
const cursorDir =
|
|
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
|
-
|
|
1565
|
+
path8.join(HOME, ".config", "Code", "User", "mcp.json"),
|
|
1562
1566
|
// Linux
|
|
1563
|
-
|
|
1567
|
+
path8.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
|
|
1564
1568
|
// macOS
|
|
1565
|
-
|
|
1569
|
+
path8.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
|
|
1566
1570
|
// Windows
|
|
1567
|
-
|
|
1571
|
+
path8.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
|
|
1568
1572
|
];
|
|
1569
1573
|
for (const c of candidates) {
|
|
1570
|
-
if (existsSync7(
|
|
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(
|
|
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 =
|
|
1596
|
+
const p = path8.join(HOME, ".claude.json");
|
|
1593
1597
|
if (existsSync7(p)) return p;
|
|
1594
|
-
const p2 =
|
|
1595
|
-
if (existsSync7(
|
|
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() ??
|
|
1600
|
-
if (!existsSync7(cfgPath) && !existsSync7(
|
|
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
|
-
|
|
1619
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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:
|
|
1771
|
-
{ client: "Cursor", path:
|
|
1772
|
-
{ client: "VS Code", path:
|
|
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 =
|
|
1799
|
+
const dir = path9.join(paths.runtimeDir, "enforcement");
|
|
1796
1800
|
await mkdir4(dir, { recursive: true });
|
|
1797
|
-
const file =
|
|
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:
|
|
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(
|
|
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
|
|
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(
|
|
1982
|
-
if (existsSync9(
|
|
1983
|
-
if (existsSync9(
|
|
1984
|
-
if (existsSync9(
|
|
1985
|
-
if (existsSync9(
|
|
1986
|
-
if (existsSync9(
|
|
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(
|
|
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 =
|
|
2047
|
+
const rel = path10.relative(root, path10.join(dir, entry.name));
|
|
2044
2048
|
results.push(rel);
|
|
2045
|
-
await walk(
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
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
|
|
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(
|
|
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.
|
|
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 =
|
|
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(
|
|
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 ${
|
|
3662
|
+
ui.success(`Created ${path12.relative(root, paths.projectContext)}`);
|
|
3659
3663
|
}
|
|
3660
3664
|
}
|
|
3661
3665
|
const configExists = existsSync11(
|
|
3662
|
-
|
|
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 =
|
|
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(
|
|
3737
|
+
await mkdir6(path12.dirname(ciPath), { recursive: true });
|
|
3734
3738
|
await writeFile7(ciPath, CI_WORKFLOW, "utf8");
|
|
3735
|
-
ui.success(`Created ${
|
|
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 (${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
3923
|
-
const hasTurboJson = existsSync11(
|
|
3924
|
-
const hasNxJson = existsSync11(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
4231
|
+
return path13.join(home, ".claude", "settings.json");
|
|
4228
4232
|
}
|
|
4229
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
4485
|
+
const cacheDir = path15.join(paths.haiveDir, ".cache");
|
|
4482
4486
|
await mkdir9(cacheDir, { recursive: true });
|
|
4483
4487
|
await appendFile(
|
|
4484
|
-
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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) =>
|
|
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 =
|
|
8132
|
-
const baseName =
|
|
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) =>
|
|
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(
|
|
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.
|
|
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
|
|
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 = [
|
|
9907
|
+
bridgeTargets = [path17.resolve(opts.bridgeFile)];
|
|
9886
9908
|
} else {
|
|
9887
|
-
const agentsMd =
|
|
9888
|
-
bridgeTargets = [
|
|
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 =
|
|
10042
|
+
const teamDir = path17.join(paths.memoriesDir, "team");
|
|
10021
10043
|
await mkdir11(teamDir, { recursive: true });
|
|
10022
10044
|
await writeFile15(
|
|
10023
|
-
|
|
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 =
|
|
10111
|
+
const teamDir = path17.join(paths.memoriesDir, "team");
|
|
10090
10112
|
await mkdir11(teamDir, { recursive: true });
|
|
10091
10113
|
await writeFile15(
|
|
10092
|
-
|
|
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(`${
|
|
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(`${
|
|
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 ${
|
|
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 ${
|
|
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
|
|
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(
|
|
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) ${
|
|
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(
|
|
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 ${
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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 ${
|
|
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
|
|
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(
|
|
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
|
|
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 ${
|
|
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
|
|
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(
|
|
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
|
|
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 ${
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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: ${
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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:")} ${
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 ??
|
|
12114
|
+
const pkgName = opts.package ?? path37.basename(path37.dirname(changelogPath));
|
|
12093
12115
|
const scope = opts.scope ?? "team";
|
|
12094
|
-
const teamDir =
|
|
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:** \`${
|
|
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: [
|
|
12154
|
+
paths: [path37.relative(root, changelogPath)],
|
|
12133
12155
|
topic: `changelog-${pkgName}-${entry.version}`
|
|
12134
12156
|
});
|
|
12135
12157
|
await writeFile26(
|
|
12136
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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=${
|
|
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(
|
|
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=${
|
|
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 (!
|
|
12611
|
-
const rel =
|
|
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
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
12770
|
-
const base =
|
|
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
|
|
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 =
|
|
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 =
|
|
12837
|
+
const sharedDir = path41.join(absPath, ".ai", "memories", "shared");
|
|
12816
12838
|
await mkdir17(sharedDir, { recursive: true });
|
|
12817
12839
|
await writeFile29(
|
|
12818
|
-
|
|
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
|
-
|
|
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": "${
|
|
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 =
|
|
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 =
|
|
12891
|
-
const destDir =
|
|
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 =
|
|
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",
|
|
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 =
|
|
12954
|
-
const hubSharedDir =
|
|
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 =
|
|
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 =
|
|
12970
|
-
const destDir =
|
|
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 =
|
|
12978
|
-
const destPath =
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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 ${
|
|
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 (
|
|
13402
|
+
if (path44.isAbsolute(candidate)) return candidate;
|
|
13381
13403
|
const projectRoot = findProjectRoot41(process.cwd());
|
|
13382
|
-
return
|
|
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 =
|
|
13391
|
-
const reportFile =
|
|
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
|
-
|
|
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
|
-
|
|
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 |
|
|
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 |
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
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 ?
|
|
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(
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
13976
|
+
await mkdir20(path46.dirname(file), { recursive: true });
|
|
13953
13977
|
if (existsSync66(file)) {
|
|
13954
|
-
skipped.push({ query: s.query, reason: `file already exists at ${
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
14827
|
+
const isHaiveWorkspace = (await readJson(path48.join(root, "package.json")))?.name === "haive-monorepo";
|
|
14804
14828
|
if (!isHaiveWorkspace) return findings;
|
|
14805
|
-
const cliDist =
|
|
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) =>
|
|
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(
|
|
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(
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 (${
|
|
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 =
|
|
15621
|
+
const cacheDir = path54.join(paths.haiveDir, ".cache");
|
|
15598
15622
|
if (existsSync75(cacheDir)) {
|
|
15599
|
-
if (opts.dryRun) ui.info(`would clean ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
16108
|
+
const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
|
|
16085
16109
|
await mkdir21(dir, { recursive: true });
|
|
16086
|
-
const file =
|
|
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.
|
|
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(
|
|
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(
|
|
16527
|
+
removed += await cleanupEnforcementDir(path54.join(runtimeDir, entry.name));
|
|
16504
16528
|
continue;
|
|
16505
16529
|
}
|
|
16506
|
-
await rm3(
|
|
16530
|
+
await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
16507
16531
|
removed++;
|
|
16508
16532
|
}
|
|
16509
|
-
await writeFile37(
|
|
16510
|
-
if (!existsSync75(
|
|
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
|
-
|
|
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(
|
|
16549
|
+
await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
16526
16550
|
removed++;
|
|
16527
16551
|
}
|
|
16528
|
-
await writeFile37(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
16986
|
-
if (!existsSync75(
|
|
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 =
|
|
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 =
|
|
17034
|
-
await mkdir21(
|
|
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 ${
|
|
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 (!
|
|
17171
|
-
return
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
17442
|
+
const outDir = path55.resolve(root, opts.outDir ?? ".ai/generated");
|
|
17419
17443
|
await mkdir23(outDir, { recursive: true });
|
|
17420
|
-
const outPath =
|
|
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): ${
|
|
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
|
|
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 =
|
|
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 ${
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
17882
|
+
globalModules = path57.join(path57.dirname(path57.dirname(process.execPath)), "lib", "node_modules");
|
|
17847
17883
|
}
|
|
17848
|
-
const globalHive =
|
|
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 =
|
|
17857
|
-
if (!existsSync79(from) || !existsSync79(
|
|
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(
|
|
17895
|
+
linked.push(path57.relative(globalModules, toDistDir));
|
|
17860
17896
|
};
|
|
17861
17897
|
for (const pkg of ["cli", "mcp"]) {
|
|
17862
|
-
await copyDist(pkg,
|
|
17898
|
+
await copyDist(pkg, path57.join(globalHive, pkg, "dist"));
|
|
17863
17899
|
for (const nested of ["core", "embeddings"]) {
|
|
17864
|
-
await copyDist(nested,
|
|
17900
|
+
await copyDist(nested, path57.join(globalHive, pkg, "node_modules", "@hiveai", nested, "dist"));
|
|
17865
17901
|
}
|
|
17866
17902
|
}
|
|
17867
|
-
await copyDist("core",
|
|
17903
|
+
await copyDist("core", path57.join(globalHive, "core", "dist"));
|
|
17868
17904
|
let version = "unknown";
|
|
17869
17905
|
try {
|
|
17870
|
-
version = JSON.parse(await readFile27(
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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.
|
|
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);
|