@hiveai/cli 0.18.0 → 0.19.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 +632 -635
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1378,14 +1378,15 @@ async function reportIndexStatus(root, paths, asJson) {
|
|
|
1378
1378
|
|
|
1379
1379
|
// src/commands/init.ts
|
|
1380
1380
|
import { execFile as execFile2 } from "child_process";
|
|
1381
|
-
import { mkdir as
|
|
1382
|
-
import { existsSync as
|
|
1383
|
-
import
|
|
1381
|
+
import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
|
|
1382
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1383
|
+
import path11 from "path";
|
|
1384
1384
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1385
1385
|
import { promisify as promisify2 } from "util";
|
|
1386
1386
|
import "commander";
|
|
1387
1387
|
import {
|
|
1388
1388
|
AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
|
|
1389
|
+
BRIDGE_TARGETS,
|
|
1389
1390
|
buildCodeMap as buildCodeMap3,
|
|
1390
1391
|
buildFrontmatter as buildFrontmatter2,
|
|
1391
1392
|
detectStacksFromManifests,
|
|
@@ -1397,20 +1398,101 @@ import {
|
|
|
1397
1398
|
serializeMemory as serializeMemory3
|
|
1398
1399
|
} from "@hiveai/core";
|
|
1399
1400
|
|
|
1401
|
+
// src/utils/bridge-files.ts
|
|
1402
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
1403
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1404
|
+
import path6 from "path";
|
|
1405
|
+
import {
|
|
1406
|
+
BRIDGE_MARKERS,
|
|
1407
|
+
generateBridges,
|
|
1408
|
+
isRetiredMemory,
|
|
1409
|
+
loadMemoriesFromDir as loadMemoriesFromDir4
|
|
1410
|
+
} from "@hiveai/core";
|
|
1411
|
+
async function writeBridgeFiles(root, paths, opts) {
|
|
1412
|
+
const result = { created: [], updated: [], unchanged: [] };
|
|
1413
|
+
if (!existsSync6(paths.memoriesDir)) return result;
|
|
1414
|
+
const allLoaded = await loadMemoriesFromDir4(paths.memoriesDir);
|
|
1415
|
+
const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory(m.frontmatter, m.body));
|
|
1416
|
+
const sensors = [];
|
|
1417
|
+
for (const m of memories) {
|
|
1418
|
+
const sensor = m.frontmatter.sensor;
|
|
1419
|
+
if (!sensor || sensor.severity !== "block") continue;
|
|
1420
|
+
sensors.push({
|
|
1421
|
+
id: m.frontmatter.id,
|
|
1422
|
+
severity: "block",
|
|
1423
|
+
message: sensor.message,
|
|
1424
|
+
...sensor.pattern ? { pattern: sensor.pattern } : {},
|
|
1425
|
+
paths: sensor.paths.length > 0 ? sensor.paths : m.frontmatter.anchor.paths
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
const maxMemories = Math.max(1, opts.maxMemories ?? 8);
|
|
1429
|
+
const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
|
|
1430
|
+
for (const output of outputs) {
|
|
1431
|
+
const targetFile = path6.join(root, output.path);
|
|
1432
|
+
const fileExists = existsSync6(targetFile);
|
|
1433
|
+
if (opts.onlyExisting && !fileExists) continue;
|
|
1434
|
+
if (opts.dryRun) {
|
|
1435
|
+
(fileExists ? result.updated : result.created).push(output.path);
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
await mkdir2(path6.dirname(targetFile), { recursive: true });
|
|
1439
|
+
if (!fileExists) {
|
|
1440
|
+
await writeFile3(targetFile, output.content, "utf8");
|
|
1441
|
+
result.created.push(output.path);
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
let existing = (await readFile3(targetFile, "utf8")).replace(/\r\n/g, "\n");
|
|
1445
|
+
const withMemories = replaceMarkerBlock(
|
|
1446
|
+
existing,
|
|
1447
|
+
BRIDGE_MARKERS.memoriesStart,
|
|
1448
|
+
BRIDGE_MARKERS.memoriesEnd,
|
|
1449
|
+
extractMarkerBlock(output.content, BRIDGE_MARKERS.memoriesStart, BRIDGE_MARKERS.memoriesEnd)
|
|
1450
|
+
);
|
|
1451
|
+
const sensorsBlockContent = extractMarkerBlock(
|
|
1452
|
+
output.content,
|
|
1453
|
+
BRIDGE_MARKERS.sensorsStart,
|
|
1454
|
+
BRIDGE_MARKERS.sensorsEnd
|
|
1455
|
+
);
|
|
1456
|
+
const withSensors = sensorsBlockContent ? replaceMarkerBlock(withMemories, BRIDGE_MARKERS.sensorsStart, BRIDGE_MARKERS.sensorsEnd, sensorsBlockContent) : withMemories;
|
|
1457
|
+
if (withSensors === existing) {
|
|
1458
|
+
result.unchanged.push(output.path);
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
await writeFile3(targetFile, withSensors, "utf8");
|
|
1462
|
+
result.updated.push(output.path);
|
|
1463
|
+
}
|
|
1464
|
+
return result;
|
|
1465
|
+
}
|
|
1466
|
+
function extractMarkerBlock(text, startMarker, endMarker) {
|
|
1467
|
+
const startIdx = text.indexOf(startMarker);
|
|
1468
|
+
const endIdx = text.indexOf(endMarker);
|
|
1469
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;
|
|
1470
|
+
return text.slice(startIdx, endIdx + endMarker.length);
|
|
1471
|
+
}
|
|
1472
|
+
function replaceMarkerBlock(existing, startMarker, endMarker, replacement) {
|
|
1473
|
+
if (!replacement) return existing;
|
|
1474
|
+
const startIdx = existing.indexOf(startMarker);
|
|
1475
|
+
const endIdx = existing.indexOf(endMarker);
|
|
1476
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
1477
|
+
return existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + replacement + "\n";
|
|
1478
|
+
}
|
|
1479
|
+
return existing.slice(0, startIdx) + replacement + existing.slice(endIdx + endMarker.length);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1400
1482
|
// src/commands/agent.ts
|
|
1401
1483
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1402
|
-
import { existsSync as
|
|
1403
|
-
import { mkdir as
|
|
1484
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1485
|
+
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
1404
1486
|
import os2 from "os";
|
|
1405
|
-
import
|
|
1487
|
+
import path8 from "path";
|
|
1406
1488
|
import { createInterface } from "readline/promises";
|
|
1407
1489
|
import "commander";
|
|
1408
1490
|
import { findProjectRoot as findProjectRoot6, resolveHaivePaths as resolveHaivePaths5 } from "@hiveai/core";
|
|
1409
1491
|
|
|
1410
1492
|
// src/commands/init-mcp-setup.ts
|
|
1411
|
-
import { readFile as
|
|
1412
|
-
import { existsSync as
|
|
1413
|
-
import
|
|
1493
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1494
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1495
|
+
import path7 from "path";
|
|
1414
1496
|
import os from "os";
|
|
1415
1497
|
var HOME = os.homedir();
|
|
1416
1498
|
var HAIVE_MCP_ENTRY = {
|
|
@@ -1425,38 +1507,38 @@ function projectMcpEntry(root) {
|
|
|
1425
1507
|
};
|
|
1426
1508
|
}
|
|
1427
1509
|
function cursorMcpPath() {
|
|
1428
|
-
return
|
|
1510
|
+
return path7.join(HOME, ".cursor", "mcp.json");
|
|
1429
1511
|
}
|
|
1430
1512
|
async function configureCursor() {
|
|
1431
1513
|
const mcpPath = cursorMcpPath();
|
|
1432
|
-
const cursorDir =
|
|
1433
|
-
if (!
|
|
1514
|
+
const cursorDir = path7.join(HOME, ".cursor");
|
|
1515
|
+
if (!existsSync7(cursorDir)) return { client: "Cursor", status: "not_installed" };
|
|
1434
1516
|
let config = {};
|
|
1435
|
-
if (
|
|
1517
|
+
if (existsSync7(mcpPath)) {
|
|
1436
1518
|
try {
|
|
1437
|
-
config = JSON.parse(await
|
|
1519
|
+
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1438
1520
|
} catch {
|
|
1439
1521
|
}
|
|
1440
1522
|
}
|
|
1441
1523
|
config.mcpServers ??= {};
|
|
1442
1524
|
if (config.mcpServers["haive"]) return { client: "Cursor", status: "already_configured" };
|
|
1443
1525
|
config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
|
|
1444
|
-
await
|
|
1445
|
-
await
|
|
1526
|
+
await mkdir3(cursorDir, { recursive: true });
|
|
1527
|
+
await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
|
|
1446
1528
|
return { client: "Cursor", status: "configured", path: mcpPath };
|
|
1447
1529
|
}
|
|
1448
1530
|
function vscodeMcpPath() {
|
|
1449
1531
|
const candidates = [
|
|
1450
|
-
|
|
1532
|
+
path7.join(HOME, ".config", "Code", "User", "mcp.json"),
|
|
1451
1533
|
// Linux
|
|
1452
|
-
|
|
1534
|
+
path7.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
|
|
1453
1535
|
// macOS
|
|
1454
|
-
|
|
1536
|
+
path7.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
|
|
1455
1537
|
// Windows
|
|
1456
|
-
|
|
1538
|
+
path7.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
|
|
1457
1539
|
];
|
|
1458
1540
|
for (const c of candidates) {
|
|
1459
|
-
if (
|
|
1541
|
+
if (existsSync7(path7.dirname(c))) return c;
|
|
1460
1542
|
}
|
|
1461
1543
|
return null;
|
|
1462
1544
|
}
|
|
@@ -1464,51 +1546,51 @@ async function configureVSCode() {
|
|
|
1464
1546
|
const mcpPath = vscodeMcpPath();
|
|
1465
1547
|
if (!mcpPath) return { client: "VS Code", status: "not_installed" };
|
|
1466
1548
|
let config = {};
|
|
1467
|
-
if (
|
|
1549
|
+
if (existsSync7(mcpPath)) {
|
|
1468
1550
|
try {
|
|
1469
|
-
config = JSON.parse(await
|
|
1551
|
+
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1470
1552
|
} catch {
|
|
1471
1553
|
}
|
|
1472
1554
|
}
|
|
1473
1555
|
config.servers ??= {};
|
|
1474
1556
|
if (config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
|
|
1475
1557
|
config.servers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
|
|
1476
|
-
await
|
|
1477
|
-
await
|
|
1558
|
+
await mkdir3(path7.dirname(mcpPath), { recursive: true });
|
|
1559
|
+
await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
|
|
1478
1560
|
return { client: "VS Code", status: "configured", path: mcpPath };
|
|
1479
1561
|
}
|
|
1480
1562
|
function claudeConfigPath() {
|
|
1481
|
-
const p =
|
|
1482
|
-
if (
|
|
1483
|
-
const p2 =
|
|
1484
|
-
if (
|
|
1563
|
+
const p = path7.join(HOME, ".claude.json");
|
|
1564
|
+
if (existsSync7(p)) return p;
|
|
1565
|
+
const p2 = path7.join(HOME, ".config", "claude", "claude.json");
|
|
1566
|
+
if (existsSync7(path7.dirname(p2))) return p2;
|
|
1485
1567
|
return null;
|
|
1486
1568
|
}
|
|
1487
1569
|
async function configureClaude() {
|
|
1488
|
-
const cfgPath = claudeConfigPath() ??
|
|
1489
|
-
if (!
|
|
1570
|
+
const cfgPath = claudeConfigPath() ?? path7.join(HOME, ".claude.json");
|
|
1571
|
+
if (!existsSync7(cfgPath) && !existsSync7(path7.join(HOME, ".claude"))) {
|
|
1490
1572
|
return { client: "Claude Code", status: "not_installed" };
|
|
1491
1573
|
}
|
|
1492
1574
|
let config = {};
|
|
1493
|
-
if (
|
|
1575
|
+
if (existsSync7(cfgPath)) {
|
|
1494
1576
|
try {
|
|
1495
|
-
config = JSON.parse(await
|
|
1577
|
+
config = JSON.parse(await readFile4(cfgPath, "utf8"));
|
|
1496
1578
|
} catch {
|
|
1497
1579
|
}
|
|
1498
1580
|
}
|
|
1499
1581
|
config.mcpServers ??= {};
|
|
1500
1582
|
if (config.mcpServers["haive"]) return { client: "Claude Code", status: "already_configured" };
|
|
1501
1583
|
config.mcpServers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
|
|
1502
|
-
await
|
|
1584
|
+
await writeFile4(cfgPath, JSON.stringify(config, null, 2), "utf8");
|
|
1503
1585
|
return { client: "Claude Code", status: "configured", path: cfgPath };
|
|
1504
1586
|
}
|
|
1505
1587
|
function windsurfMcpPath() {
|
|
1506
1588
|
const candidates = [
|
|
1507
|
-
|
|
1508
|
-
|
|
1589
|
+
path7.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
|
|
1590
|
+
path7.join(HOME, ".windsurf", "mcp.json")
|
|
1509
1591
|
];
|
|
1510
1592
|
for (const c of candidates) {
|
|
1511
|
-
if (
|
|
1593
|
+
if (existsSync7(path7.dirname(c))) return c;
|
|
1512
1594
|
}
|
|
1513
1595
|
return null;
|
|
1514
1596
|
}
|
|
@@ -1516,17 +1598,17 @@ async function configureWindsurf() {
|
|
|
1516
1598
|
const mcpPath = windsurfMcpPath();
|
|
1517
1599
|
if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
|
|
1518
1600
|
let config = {};
|
|
1519
|
-
if (
|
|
1601
|
+
if (existsSync7(mcpPath)) {
|
|
1520
1602
|
try {
|
|
1521
|
-
config = JSON.parse(await
|
|
1603
|
+
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1522
1604
|
} catch {
|
|
1523
1605
|
}
|
|
1524
1606
|
}
|
|
1525
1607
|
config.mcpServers ??= {};
|
|
1526
1608
|
if (config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
|
|
1527
1609
|
config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
|
|
1528
|
-
await
|
|
1529
|
-
await
|
|
1610
|
+
await mkdir3(path7.dirname(mcpPath), { recursive: true });
|
|
1611
|
+
await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
|
|
1530
1612
|
return { client: "Windsurf", status: "configured", path: mcpPath };
|
|
1531
1613
|
}
|
|
1532
1614
|
async function autoConfigureMcpClients() {
|
|
@@ -1546,51 +1628,51 @@ async function configureProjectMcpClients(root) {
|
|
|
1546
1628
|
const entry = projectMcpEntry(root);
|
|
1547
1629
|
const results = [];
|
|
1548
1630
|
try {
|
|
1549
|
-
const cursorPath =
|
|
1631
|
+
const cursorPath = path7.join(root, ".cursor", "mcp.json");
|
|
1550
1632
|
let config = {};
|
|
1551
|
-
if (
|
|
1633
|
+
if (existsSync7(cursorPath)) {
|
|
1552
1634
|
try {
|
|
1553
|
-
config = JSON.parse(await
|
|
1635
|
+
config = JSON.parse(await readFile4(cursorPath, "utf8"));
|
|
1554
1636
|
} catch {
|
|
1555
1637
|
}
|
|
1556
1638
|
}
|
|
1557
1639
|
config.mcpServers ??= {};
|
|
1558
1640
|
config.mcpServers["haive"] = entry;
|
|
1559
|
-
await
|
|
1560
|
-
await
|
|
1641
|
+
await mkdir3(path7.dirname(cursorPath), { recursive: true });
|
|
1642
|
+
await writeFile4(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1561
1643
|
results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
|
|
1562
1644
|
} catch (err) {
|
|
1563
1645
|
results.push({ client: "Cursor (project)", status: "error", error: String(err) });
|
|
1564
1646
|
}
|
|
1565
1647
|
try {
|
|
1566
|
-
const vscodePath =
|
|
1648
|
+
const vscodePath = path7.join(root, ".vscode", "mcp.json");
|
|
1567
1649
|
let config = {};
|
|
1568
|
-
if (
|
|
1650
|
+
if (existsSync7(vscodePath)) {
|
|
1569
1651
|
try {
|
|
1570
|
-
config = JSON.parse(await
|
|
1652
|
+
config = JSON.parse(await readFile4(vscodePath, "utf8"));
|
|
1571
1653
|
} catch {
|
|
1572
1654
|
}
|
|
1573
1655
|
}
|
|
1574
1656
|
config.servers ??= {};
|
|
1575
1657
|
config.servers["haive"] = { ...entry, type: "stdio" };
|
|
1576
|
-
await
|
|
1577
|
-
await
|
|
1658
|
+
await mkdir3(path7.dirname(vscodePath), { recursive: true });
|
|
1659
|
+
await writeFile4(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1578
1660
|
results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
|
|
1579
1661
|
} catch (err) {
|
|
1580
1662
|
results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
|
|
1581
1663
|
}
|
|
1582
1664
|
try {
|
|
1583
|
-
const mcpPath =
|
|
1665
|
+
const mcpPath = path7.join(root, ".mcp.json");
|
|
1584
1666
|
let config = {};
|
|
1585
|
-
if (
|
|
1667
|
+
if (existsSync7(mcpPath)) {
|
|
1586
1668
|
try {
|
|
1587
|
-
config = JSON.parse(await
|
|
1669
|
+
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1588
1670
|
} catch {
|
|
1589
1671
|
}
|
|
1590
1672
|
}
|
|
1591
1673
|
config.mcpServers ??= {};
|
|
1592
1674
|
config.mcpServers["haive"] = { ...entry, type: "stdio" };
|
|
1593
|
-
await
|
|
1675
|
+
await writeFile4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1594
1676
|
results.push({ client: "Claude Code (project)", status: "configured", path: mcpPath });
|
|
1595
1677
|
} catch (err) {
|
|
1596
1678
|
results.push({ client: "Claude Code (project)", status: "error", error: String(err) });
|
|
@@ -1656,9 +1738,9 @@ async function detectAgentMode(dir) {
|
|
|
1656
1738
|
const root = findProjectRoot6(dir);
|
|
1657
1739
|
const paths = resolveHaivePaths5(root);
|
|
1658
1740
|
const projectMcp = [
|
|
1659
|
-
{ client: "Claude Code", path:
|
|
1660
|
-
{ client: "Cursor", path:
|
|
1661
|
-
{ client: "VS Code", path:
|
|
1741
|
+
{ client: "Claude Code", path: path8.join(root, ".mcp.json"), present: existsSync8(path8.join(root, ".mcp.json")) },
|
|
1742
|
+
{ client: "Cursor", path: path8.join(root, ".cursor", "mcp.json"), present: existsSync8(path8.join(root, ".cursor", "mcp.json")) },
|
|
1743
|
+
{ client: "VS Code", path: path8.join(root, ".vscode", "mcp.json"), present: existsSync8(path8.join(root, ".vscode", "mcp.json")) }
|
|
1662
1744
|
];
|
|
1663
1745
|
const installedAgents = [
|
|
1664
1746
|
{ agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
|
|
@@ -1673,7 +1755,7 @@ async function detectAgentMode(dir) {
|
|
|
1673
1755
|
const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `haive run -- ${wrapperAgent.command}` : 'haive briefing --task "..." --files "..."';
|
|
1674
1756
|
return {
|
|
1675
1757
|
root,
|
|
1676
|
-
initialized:
|
|
1758
|
+
initialized: existsSync8(paths.haiveDir),
|
|
1677
1759
|
project_mcp: projectMcp,
|
|
1678
1760
|
installed_agents: installedAgents,
|
|
1679
1761
|
recommended_mode: recommendedMode,
|
|
@@ -1681,9 +1763,9 @@ async function detectAgentMode(dir) {
|
|
|
1681
1763
|
};
|
|
1682
1764
|
}
|
|
1683
1765
|
async function writeAgentModeRecord(paths, detection, skippedReason) {
|
|
1684
|
-
const dir =
|
|
1685
|
-
await
|
|
1686
|
-
const file =
|
|
1766
|
+
const dir = path8.join(paths.runtimeDir, "enforcement");
|
|
1767
|
+
await mkdir4(dir, { recursive: true });
|
|
1768
|
+
const file = path8.join(dir, "agent-mode.json");
|
|
1687
1769
|
const record = {
|
|
1688
1770
|
selected_mode: detection.recommended_mode,
|
|
1689
1771
|
recommended_command: detection.recommended_command,
|
|
@@ -1696,7 +1778,7 @@ async function writeAgentModeRecord(paths, detection, skippedReason) {
|
|
|
1696
1778
|
...skippedReason ? [skippedReason] : []
|
|
1697
1779
|
]
|
|
1698
1780
|
};
|
|
1699
|
-
await
|
|
1781
|
+
await writeFile5(file, JSON.stringify(record, null, 2) + "\n", "utf8");
|
|
1700
1782
|
return file;
|
|
1701
1783
|
}
|
|
1702
1784
|
async function confirmGlobalSetup() {
|
|
@@ -1724,7 +1806,7 @@ async function configureCodexIfAvailable(root) {
|
|
|
1724
1806
|
"mcp",
|
|
1725
1807
|
"--stdio"
|
|
1726
1808
|
], { encoding: "utf8" });
|
|
1727
|
-
if (result.status === 0) return { client: "Codex", status: "configured", path:
|
|
1809
|
+
if (result.status === 0) return { client: "Codex", status: "configured", path: path8.join(os2.homedir(), ".codex", "config.toml") };
|
|
1728
1810
|
return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
|
|
1729
1811
|
}
|
|
1730
1812
|
function commandExists(command) {
|
|
@@ -1751,7 +1833,7 @@ function printDetection(detection, json) {
|
|
|
1751
1833
|
console.log(ui.dim(` root: ${detection.root}`));
|
|
1752
1834
|
console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
|
|
1753
1835
|
for (const cfg of detection.project_mcp) {
|
|
1754
|
-
console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(
|
|
1836
|
+
console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path8.relative(detection.root, cfg.path))}`);
|
|
1755
1837
|
}
|
|
1756
1838
|
for (const agent of detection.installed_agents) {
|
|
1757
1839
|
const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
|
|
@@ -1779,9 +1861,9 @@ function printSetupResult(result) {
|
|
|
1779
1861
|
}
|
|
1780
1862
|
|
|
1781
1863
|
// src/commands/init-bootstrap.ts
|
|
1782
|
-
import { readdir, readFile as
|
|
1783
|
-
import { existsSync as
|
|
1784
|
-
import
|
|
1864
|
+
import { readdir, readFile as readFile5 } from "fs/promises";
|
|
1865
|
+
import { existsSync as existsSync9, readdirSync } from "fs";
|
|
1866
|
+
import path9 from "path";
|
|
1785
1867
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
1786
1868
|
"node_modules",
|
|
1787
1869
|
"dist",
|
|
@@ -1867,12 +1949,12 @@ function detectKeyDeps(allDeps) {
|
|
|
1867
1949
|
return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
|
|
1868
1950
|
}
|
|
1869
1951
|
function detectLanguage(root) {
|
|
1870
|
-
if (
|
|
1871
|
-
if (
|
|
1872
|
-
if (
|
|
1873
|
-
if (
|
|
1874
|
-
if (
|
|
1875
|
-
if (
|
|
1952
|
+
if (existsSync9(path9.join(root, "tsconfig.json"))) return "TypeScript";
|
|
1953
|
+
if (existsSync9(path9.join(root, "pyproject.toml")) || existsSync9(path9.join(root, "setup.py"))) return "Python";
|
|
1954
|
+
if (existsSync9(path9.join(root, "go.mod"))) return "Go";
|
|
1955
|
+
if (existsSync9(path9.join(root, "pom.xml")) || existsSync9(path9.join(root, "build.gradle"))) return "Java/Kotlin";
|
|
1956
|
+
if (existsSync9(path9.join(root, "Cargo.toml"))) return "Rust";
|
|
1957
|
+
if (existsSync9(path9.join(root, "package.json"))) {
|
|
1876
1958
|
return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
|
|
1877
1959
|
}
|
|
1878
1960
|
return "Unknown";
|
|
@@ -1892,7 +1974,7 @@ function hasSourceWithExt(root, exts) {
|
|
|
1892
1974
|
if (depth <= 0) return false;
|
|
1893
1975
|
for (const entry of entries) {
|
|
1894
1976
|
if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
1895
|
-
if (scanDir(
|
|
1977
|
+
if (scanDir(path9.join(dir, entry.name), depth - 1)) return true;
|
|
1896
1978
|
}
|
|
1897
1979
|
}
|
|
1898
1980
|
return false;
|
|
@@ -1913,7 +1995,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
|
|
|
1913
1995
|
if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
|
|
1914
1996
|
if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
|
|
1915
1997
|
if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
|
|
1916
|
-
if (
|
|
1998
|
+
if (existsSync9("pom.xml")) return "Java backend";
|
|
1917
1999
|
return "Application";
|
|
1918
2000
|
}
|
|
1919
2001
|
async function scanDirs(root, maxDepth = 2) {
|
|
@@ -1929,9 +2011,9 @@ async function scanDirs(root, maxDepth = 2) {
|
|
|
1929
2011
|
for (const entry of entries) {
|
|
1930
2012
|
if (!entry.isDirectory()) continue;
|
|
1931
2013
|
if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
1932
|
-
const rel =
|
|
2014
|
+
const rel = path9.relative(root, path9.join(dir, entry.name));
|
|
1933
2015
|
results.push(rel);
|
|
1934
|
-
await walk(
|
|
2016
|
+
await walk(path9.join(dir, entry.name), depth + 1);
|
|
1935
2017
|
}
|
|
1936
2018
|
}
|
|
1937
2019
|
await walk(root, 0);
|
|
@@ -2048,10 +2130,10 @@ function readmeExcerpt(readme) {
|
|
|
2048
2130
|
}
|
|
2049
2131
|
async function generateBootstrapContext(root) {
|
|
2050
2132
|
let pkg = {};
|
|
2051
|
-
const pkgPath =
|
|
2052
|
-
if (
|
|
2133
|
+
const pkgPath = path9.join(root, "package.json");
|
|
2134
|
+
if (existsSync9(pkgPath)) {
|
|
2053
2135
|
try {
|
|
2054
|
-
pkg = JSON.parse(await
|
|
2136
|
+
pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
2055
2137
|
} catch {
|
|
2056
2138
|
}
|
|
2057
2139
|
}
|
|
@@ -2061,14 +2143,14 @@ async function generateBootstrapContext(root) {
|
|
|
2061
2143
|
const language = detectLanguage(root);
|
|
2062
2144
|
const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
|
|
2063
2145
|
const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
|
|
2064
|
-
const projectName = pkg.name ??
|
|
2146
|
+
const projectName = pkg.name ?? path9.basename(root);
|
|
2065
2147
|
const projectDesc = pkg.description ?? "";
|
|
2066
2148
|
let readmeSummary = "";
|
|
2067
2149
|
for (const name of ["README.md", "readme.md", "README"]) {
|
|
2068
|
-
const p =
|
|
2069
|
-
if (
|
|
2150
|
+
const p = path9.join(root, name);
|
|
2151
|
+
if (existsSync9(p)) {
|
|
2070
2152
|
try {
|
|
2071
|
-
const content = await
|
|
2153
|
+
const content = await readFile5(p, "utf8");
|
|
2072
2154
|
readmeSummary = readmeExcerpt(content);
|
|
2073
2155
|
break;
|
|
2074
2156
|
} catch {
|
|
@@ -2132,9 +2214,9 @@ async function generateBootstrapContext(root) {
|
|
|
2132
2214
|
}
|
|
2133
2215
|
|
|
2134
2216
|
// src/commands/init-stack-packs.ts
|
|
2135
|
-
import { mkdir as
|
|
2136
|
-
import { existsSync as
|
|
2137
|
-
import
|
|
2217
|
+
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
2218
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2219
|
+
import path10 from "path";
|
|
2138
2220
|
import {
|
|
2139
2221
|
buildFrontmatter,
|
|
2140
2222
|
memoryFilePath,
|
|
@@ -3213,7 +3295,7 @@ function autoDetectStacks(deps) {
|
|
|
3213
3295
|
async function seedStackPack(haivePaths, stack) {
|
|
3214
3296
|
const memories = PACKS[stack];
|
|
3215
3297
|
if (!memories) return { memories: 0, sensors: 0 };
|
|
3216
|
-
await
|
|
3298
|
+
await mkdir5(haivePaths.teamDir, { recursive: true });
|
|
3217
3299
|
let memCount = 0;
|
|
3218
3300
|
let sensorCount = 0;
|
|
3219
3301
|
for (const mem of memories) {
|
|
@@ -3238,12 +3320,12 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3238
3320
|
...sensor ? { sensor } : {}
|
|
3239
3321
|
});
|
|
3240
3322
|
const filePath = memoryFilePath(haivePaths, "team", fm.id);
|
|
3241
|
-
if (
|
|
3323
|
+
if (existsSync10(filePath)) continue;
|
|
3242
3324
|
const content = serializeMemory2({ frontmatter: fm, body: `${mem.body}
|
|
3243
3325
|
|
|
3244
3326
|
${SEED_FOOTER(stack)}` });
|
|
3245
|
-
await
|
|
3246
|
-
await
|
|
3327
|
+
await mkdir5(path10.dirname(filePath), { recursive: true });
|
|
3328
|
+
await writeFile6(filePath, content, "utf8");
|
|
3247
3329
|
memCount++;
|
|
3248
3330
|
if (sensor) sensorCount++;
|
|
3249
3331
|
}
|
|
@@ -3252,7 +3334,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3252
3334
|
|
|
3253
3335
|
// src/commands/init.ts
|
|
3254
3336
|
var execFileAsync = promisify2(execFile2);
|
|
3255
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3337
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.19.0"}`;
|
|
3256
3338
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3257
3339
|
|
|
3258
3340
|
> Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -3274,28 +3356,6 @@ TODO \u2014 domain terms and what they mean here.
|
|
|
3274
3356
|
## Gotchas
|
|
3275
3357
|
TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.
|
|
3276
3358
|
`;
|
|
3277
|
-
var BRIDGE_BODY = `<!-- hAIve bridge file \u2014 do not edit by hand. -->
|
|
3278
|
-
|
|
3279
|
-
This repo uses **hAIve** for shared context. The map:
|
|
3280
|
-
|
|
3281
|
-
- \`.ai/project-context.md\` \u2014 project overview, architecture, conventions.
|
|
3282
|
-
- \`.ai/memories/\` \u2014 decisions, gotchas, conventions, failed attempts (personal/team/module).
|
|
3283
|
-
- The breadcrumbs injected below (if any) are the top current memories.
|
|
3284
|
-
|
|
3285
|
-
## Working through hAIve
|
|
3286
|
-
|
|
3287
|
-
1. **Before editing** for a goal, call \`get_briefing\` (task + files/symbols) to load ranked context \u2014 or \`mem_relevant_to\` if project context is already loaded this session.
|
|
3288
|
-
2. **When an approach fails**, call \`mem_tried\` right away so the next agent skips the dead end.
|
|
3289
|
-
3. **Before closing** a substantive session, run the \`post_task\` prompt to capture what was learned.
|
|
3290
|
-
4. **Before final response**, run \`haive enforce finish\`. If it blocks, commit/push, bump/tag shippable releases, wait for GitHub Actions to pass when applicable, then rerun it.
|
|
3291
|
-
|
|
3292
|
-
If the haive MCP server is not available, tell the developer rather than silently skipping it.
|
|
3293
|
-
|
|
3294
|
-
## Safety
|
|
3295
|
-
|
|
3296
|
-
- If \`get_briefing\` returns \`action_required\`, surface each item to the developer (use its \`developer_message\`) and wait for confirmation before changing code.
|
|
3297
|
-
- Never act autonomously on a cross-repo breaking change (dep bump, contract/API diff) \u2014 ask first.
|
|
3298
|
-
`;
|
|
3299
3359
|
var CURSOR_HAIVE_RULE_MDC = `---
|
|
3300
3360
|
description: Require hAIve MCP (get_briefing / mem_relevant_to) before substantive repo edits
|
|
3301
3361
|
alwaysApply: true
|
|
@@ -3465,7 +3525,12 @@ jobs:
|
|
|
3465
3525
|
function registerInit(program2) {
|
|
3466
3526
|
program2.command("init").description(
|
|
3467
3527
|
"Initialize a hAIve project \u2014 autopilot mode ON by default (zero human intervention).\n Auto-bootstraps project-context.md from local files and seeds detected stack packs.\n Seeds draft memories from git revert/hotfix history (--seed, on by default).\n Add --manual to control memory approval and session recaps yourself.\n Add --no-bootstrap and --stack none to disable the auto-features."
|
|
3468
|
-
).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate
|
|
3528
|
+
).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate any native agent bridge files").option(
|
|
3529
|
+
"--bridge-targets <list>",
|
|
3530
|
+
`which agent bridges to generate: 'all' (default) | comma-list.
|
|
3531
|
+
Available: ${BRIDGE_TARGETS.join(", ")}. Each carries top memories + block sensors.`,
|
|
3532
|
+
"all"
|
|
3533
|
+
).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
|
|
3469
3534
|
"--manual",
|
|
3470
3535
|
"opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
|
|
3471
3536
|
).option(
|
|
@@ -3497,7 +3562,7 @@ function registerInit(program2) {
|
|
|
3497
3562
|
"approve user-level AI client configuration prompts during agent setup",
|
|
3498
3563
|
false
|
|
3499
3564
|
).option("--json", "emit a machine-readable summary on stdout (human logs go to stderr)", false).action(async (opts) => {
|
|
3500
|
-
const root =
|
|
3565
|
+
const root = path11.resolve(opts.dir);
|
|
3501
3566
|
const paths = resolveHaivePaths6(root);
|
|
3502
3567
|
const autopilot = opts.manual !== true;
|
|
3503
3568
|
const json = opts.json === true;
|
|
@@ -3513,35 +3578,36 @@ function registerInit(program2) {
|
|
|
3513
3578
|
gitSeedsWritten: 0,
|
|
3514
3579
|
gitCommitsScanned: 0,
|
|
3515
3580
|
gitRevertsFound: 0,
|
|
3516
|
-
gitRecurring: 0
|
|
3581
|
+
gitRecurring: 0,
|
|
3582
|
+
bridgesWritten: 0
|
|
3517
3583
|
};
|
|
3518
|
-
if (
|
|
3584
|
+
if (existsSync11(paths.haiveDir)) {
|
|
3519
3585
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
3520
3586
|
}
|
|
3521
|
-
await
|
|
3522
|
-
await
|
|
3523
|
-
await
|
|
3524
|
-
await
|
|
3587
|
+
await mkdir6(paths.personalDir, { recursive: true });
|
|
3588
|
+
await mkdir6(paths.teamDir, { recursive: true });
|
|
3589
|
+
await mkdir6(paths.moduleDir, { recursive: true });
|
|
3590
|
+
await mkdir6(paths.modulesContextDir, { recursive: true });
|
|
3525
3591
|
await ensureAiRuntimeLayout(paths.runtimeDir);
|
|
3526
|
-
await ensureAiCacheLayout(
|
|
3527
|
-
if (!
|
|
3592
|
+
await ensureAiCacheLayout(path11.join(paths.haiveDir, ".cache"));
|
|
3593
|
+
if (!existsSync11(paths.projectContext)) {
|
|
3528
3594
|
if (wantBootstrap) {
|
|
3529
3595
|
ui.info("Bootstrapping project context from local files\u2026");
|
|
3530
3596
|
try {
|
|
3531
3597
|
const context = await generateBootstrapContext(root);
|
|
3532
|
-
await
|
|
3598
|
+
await writeFile7(paths.projectContext, context, "utf8");
|
|
3533
3599
|
ui.success("Created .ai/project-context.md (auto-bootstrapped from local files)");
|
|
3534
3600
|
} catch (err) {
|
|
3535
3601
|
ui.warn(`Bootstrap failed (${String(err)}) \u2014 writing default template instead`);
|
|
3536
|
-
await
|
|
3602
|
+
await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
3537
3603
|
}
|
|
3538
3604
|
} else {
|
|
3539
|
-
await
|
|
3540
|
-
ui.success(`Created ${
|
|
3605
|
+
await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
3606
|
+
ui.success(`Created ${path11.relative(root, paths.projectContext)}`);
|
|
3541
3607
|
}
|
|
3542
3608
|
}
|
|
3543
|
-
const configExists =
|
|
3544
|
-
|
|
3609
|
+
const configExists = existsSync11(
|
|
3610
|
+
path11.join(paths.haiveDir, "haive.config.json")
|
|
3545
3611
|
);
|
|
3546
3612
|
if (!configExists) {
|
|
3547
3613
|
await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
|
|
@@ -3549,13 +3615,6 @@ function registerInit(program2) {
|
|
|
3549
3615
|
`Created .ai/haive.config.json (mode: ${autopilot ? "autopilot" : "standard"})`
|
|
3550
3616
|
);
|
|
3551
3617
|
}
|
|
3552
|
-
if (opts.bridges) {
|
|
3553
|
-
await writeBridge(root, "CLAUDE.md");
|
|
3554
|
-
await writeBridge(root, "AGENTS.md");
|
|
3555
|
-
await writeBridge(root, ".cursorrules");
|
|
3556
|
-
await writeBridge(root, path10.join(".github", "copilot-instructions.md"));
|
|
3557
|
-
await writeCursorHaiveRule(root);
|
|
3558
|
-
}
|
|
3559
3618
|
const stacksToSeed = await resolveStacksToSeed(root, wantStack);
|
|
3560
3619
|
if (stacksToSeed.length > 0) {
|
|
3561
3620
|
let totalSeeded = 0;
|
|
@@ -3597,15 +3656,31 @@ function registerInit(program2) {
|
|
|
3597
3656
|
ui.info("Git seeding: no revert/hotfix signals found \u2014 run `haive memory seed-git` later.");
|
|
3598
3657
|
}
|
|
3599
3658
|
}
|
|
3659
|
+
if (opts.bridges) {
|
|
3660
|
+
const targets = resolveBridgeTargets(opts.bridgeTargets);
|
|
3661
|
+
const res = await writeBridgeFiles(root, paths, { targets });
|
|
3662
|
+
await writeCursorHaiveRule(root);
|
|
3663
|
+
const made = res.created.length + res.updated.length;
|
|
3664
|
+
report.bridgesWritten = made;
|
|
3665
|
+
if (res.created.length > 0) {
|
|
3666
|
+
ui.success(`Generated ${res.created.length} agent bridge(s): ${res.created.join(", ")}`);
|
|
3667
|
+
}
|
|
3668
|
+
if (res.updated.length > 0) {
|
|
3669
|
+
ui.info(`Refreshed ${res.updated.length} existing bridge(s): ${res.updated.join(", ")}`);
|
|
3670
|
+
}
|
|
3671
|
+
if (made === 0) {
|
|
3672
|
+
ui.info("Bridges already up to date.");
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3600
3675
|
const wantCi = opts.withCi || autopilot;
|
|
3601
3676
|
if (wantCi) {
|
|
3602
|
-
const ciPath =
|
|
3603
|
-
if (
|
|
3677
|
+
const ciPath = path11.join(root, ".github", "workflows", "haive-sync.yml");
|
|
3678
|
+
if (existsSync11(ciPath)) {
|
|
3604
3679
|
ui.info("CI workflow already exists \u2014 skipped");
|
|
3605
3680
|
} else {
|
|
3606
|
-
await
|
|
3607
|
-
await
|
|
3608
|
-
ui.success(`Created ${
|
|
3681
|
+
await mkdir6(path11.dirname(ciPath), { recursive: true });
|
|
3682
|
+
await writeFile7(ciPath, CI_WORKFLOW, "utf8");
|
|
3683
|
+
ui.success(`Created ${path11.relative(root, ciPath)}`);
|
|
3609
3684
|
}
|
|
3610
3685
|
}
|
|
3611
3686
|
if (autopilot) {
|
|
@@ -3645,7 +3720,7 @@ function registerInit(program2) {
|
|
|
3645
3720
|
interactive: process.stdin.isTTY
|
|
3646
3721
|
});
|
|
3647
3722
|
for (const r of agentSetup.project_results) {
|
|
3648
|
-
if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${
|
|
3723
|
+
if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path11.relative(root, r.path)})`);
|
|
3649
3724
|
else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
|
|
3650
3725
|
}
|
|
3651
3726
|
for (const r of agentSetup.global_results) {
|
|
@@ -3682,6 +3757,7 @@ function registerInit(program2) {
|
|
|
3682
3757
|
git_seeds_written: report.gitSeedsWritten,
|
|
3683
3758
|
git_recurring: report.gitRecurring,
|
|
3684
3759
|
bridges: opts.bridges !== false,
|
|
3760
|
+
bridges_written: report.bridgesWritten,
|
|
3685
3761
|
ci: opts.withCi || autopilot
|
|
3686
3762
|
}, null, 2));
|
|
3687
3763
|
return;
|
|
@@ -3743,57 +3819,57 @@ async function autoDetectStacksFromRoot(root) {
|
|
|
3743
3819
|
let requirementsTxt;
|
|
3744
3820
|
let goMod;
|
|
3745
3821
|
let pomXml;
|
|
3746
|
-
const pkgPath =
|
|
3747
|
-
if (
|
|
3822
|
+
const pkgPath = path11.join(root, "package.json");
|
|
3823
|
+
if (existsSync11(pkgPath)) {
|
|
3748
3824
|
try {
|
|
3749
|
-
const pkg = JSON.parse(await
|
|
3825
|
+
const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
|
|
3750
3826
|
packageJsonDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
3751
3827
|
} catch {
|
|
3752
3828
|
}
|
|
3753
3829
|
}
|
|
3754
3830
|
for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
|
|
3755
|
-
const reqPath =
|
|
3756
|
-
if (
|
|
3831
|
+
const reqPath = path11.join(root, name);
|
|
3832
|
+
if (existsSync11(reqPath)) {
|
|
3757
3833
|
try {
|
|
3758
|
-
requirementsTxt = await
|
|
3834
|
+
requirementsTxt = await readFile6(reqPath, "utf8");
|
|
3759
3835
|
break;
|
|
3760
3836
|
} catch {
|
|
3761
3837
|
}
|
|
3762
3838
|
}
|
|
3763
3839
|
}
|
|
3764
|
-
const goModPath =
|
|
3765
|
-
if (
|
|
3840
|
+
const goModPath = path11.join(root, "go.mod");
|
|
3841
|
+
if (existsSync11(goModPath)) {
|
|
3766
3842
|
try {
|
|
3767
|
-
goMod = await
|
|
3843
|
+
goMod = await readFile6(goModPath, "utf8");
|
|
3768
3844
|
} catch {
|
|
3769
3845
|
}
|
|
3770
3846
|
}
|
|
3771
|
-
const pomPath =
|
|
3772
|
-
if (
|
|
3847
|
+
const pomPath = path11.join(root, "pom.xml");
|
|
3848
|
+
if (existsSync11(pomPath)) {
|
|
3773
3849
|
try {
|
|
3774
|
-
pomXml = await
|
|
3850
|
+
pomXml = await readFile6(pomPath, "utf8");
|
|
3775
3851
|
} catch {
|
|
3776
3852
|
}
|
|
3777
3853
|
}
|
|
3778
3854
|
let composerJson;
|
|
3779
|
-
const composerPath =
|
|
3780
|
-
if (
|
|
3855
|
+
const composerPath = path11.join(root, "composer.json");
|
|
3856
|
+
if (existsSync11(composerPath)) {
|
|
3781
3857
|
try {
|
|
3782
|
-
composerJson = await
|
|
3858
|
+
composerJson = await readFile6(composerPath, "utf8");
|
|
3783
3859
|
} catch {
|
|
3784
3860
|
}
|
|
3785
3861
|
}
|
|
3786
3862
|
let gemfile;
|
|
3787
|
-
const gemfilePath =
|
|
3788
|
-
if (
|
|
3863
|
+
const gemfilePath = path11.join(root, "Gemfile");
|
|
3864
|
+
if (existsSync11(gemfilePath)) {
|
|
3789
3865
|
try {
|
|
3790
|
-
gemfile = await
|
|
3866
|
+
gemfile = await readFile6(gemfilePath, "utf8");
|
|
3791
3867
|
} catch {
|
|
3792
3868
|
}
|
|
3793
3869
|
}
|
|
3794
|
-
const hasDockerfile =
|
|
3795
|
-
const hasTurboJson =
|
|
3796
|
-
const hasNxJson =
|
|
3870
|
+
const hasDockerfile = existsSync11(path11.join(root, "Dockerfile"));
|
|
3871
|
+
const hasTurboJson = existsSync11(path11.join(root, "turbo.json"));
|
|
3872
|
+
const hasNxJson = existsSync11(path11.join(root, "nx.json"));
|
|
3797
3873
|
let hasCsproj = false;
|
|
3798
3874
|
try {
|
|
3799
3875
|
const entries = await readdir2(root);
|
|
@@ -3845,9 +3921,9 @@ async function seedFromGitHistory(root, paths, limit) {
|
|
|
3845
3921
|
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
3846
3922
|
`;
|
|
3847
3923
|
const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
|
|
3848
|
-
if (
|
|
3849
|
-
await
|
|
3850
|
-
await
|
|
3924
|
+
if (existsSync11(file)) continue;
|
|
3925
|
+
await mkdir6(path11.dirname(file), { recursive: true });
|
|
3926
|
+
await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
|
|
3851
3927
|
written++;
|
|
3852
3928
|
}
|
|
3853
3929
|
return { scanned: commits.length, found: proposals.length, recurring, written };
|
|
@@ -3886,6 +3962,9 @@ function printInitReport(r) {
|
|
|
3886
3962
|
if (r.totalMemories > 0) {
|
|
3887
3963
|
lines.push(` Total ready : ${r.totalMemories} lesson(s), ${r.totalSensors} sensor(s) \u2014 0 written by hand`);
|
|
3888
3964
|
}
|
|
3965
|
+
if (r.bridgesWritten > 0) {
|
|
3966
|
+
lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (Cursor, Cline, Copilot, Roo, Gemini, \u2026)`);
|
|
3967
|
+
}
|
|
3889
3968
|
if (lines.length === 0) return;
|
|
3890
3969
|
const width = Math.max(...lines.map((l) => l.length), 44);
|
|
3891
3970
|
const bar = "\u2500".repeat(width + 2);
|
|
@@ -3900,29 +3979,30 @@ function printInitReport(r) {
|
|
|
3900
3979
|
console.log(ui.dim(" Review draft seeds: haive memory pending (validate or delete each one)"));
|
|
3901
3980
|
}
|
|
3902
3981
|
console.log(
|
|
3903
|
-
ui.dim(" Reach:
|
|
3982
|
+
ui.dim(" Reach: bridges auto-refresh on `haive sync`; regenerate any time with `haive bridges sync --all`.")
|
|
3904
3983
|
);
|
|
3905
3984
|
}
|
|
3906
3985
|
async function writeCursorHaiveRule(root) {
|
|
3907
3986
|
const relPath = ".cursor/rules/haive-mcp-required.mdc";
|
|
3908
|
-
const target =
|
|
3909
|
-
if (
|
|
3987
|
+
const target = path11.join(root, relPath);
|
|
3988
|
+
if (existsSync11(target)) {
|
|
3910
3989
|
ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
|
|
3911
3990
|
return;
|
|
3912
3991
|
}
|
|
3913
|
-
await
|
|
3914
|
-
await
|
|
3992
|
+
await mkdir6(path11.dirname(target), { recursive: true });
|
|
3993
|
+
await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
|
|
3915
3994
|
ui.success(`Created Cursor rule ${relPath}`);
|
|
3916
3995
|
}
|
|
3917
|
-
|
|
3918
|
-
const
|
|
3919
|
-
if (
|
|
3920
|
-
|
|
3921
|
-
|
|
3996
|
+
function resolveBridgeTargets(opt) {
|
|
3997
|
+
const raw = (opt ?? "all").trim().toLowerCase();
|
|
3998
|
+
if (raw === "" || raw === "all") return [...BRIDGE_TARGETS];
|
|
3999
|
+
const requested = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
4000
|
+
const valid = requested.filter((t) => BRIDGE_TARGETS.includes(t));
|
|
4001
|
+
const invalid = requested.filter((t) => !BRIDGE_TARGETS.includes(t));
|
|
4002
|
+
if (invalid.length > 0) {
|
|
4003
|
+
ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS.join(", ")}`);
|
|
3922
4004
|
}
|
|
3923
|
-
|
|
3924
|
-
await writeFile6(target, BRIDGE_BODY, "utf8");
|
|
3925
|
-
ui.success(`Created bridge ${relPath}`);
|
|
4005
|
+
return valid.length > 0 ? valid : [...BRIDGE_TARGETS];
|
|
3926
4006
|
}
|
|
3927
4007
|
var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
|
|
3928
4008
|
|
|
@@ -3937,50 +4017,50 @@ var RUNTIME_GITIGNORE_BODY = `*
|
|
|
3937
4017
|
!README.md
|
|
3938
4018
|
`;
|
|
3939
4019
|
async function ensureAiRuntimeLayout(runtimeDir) {
|
|
3940
|
-
await
|
|
3941
|
-
const gi =
|
|
3942
|
-
if (!
|
|
3943
|
-
await
|
|
4020
|
+
await mkdir6(runtimeDir, { recursive: true });
|
|
4021
|
+
const gi = path11.join(runtimeDir, ".gitignore");
|
|
4022
|
+
if (!existsSync11(gi)) {
|
|
4023
|
+
await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
|
|
3944
4024
|
}
|
|
3945
|
-
const readme =
|
|
3946
|
-
if (!
|
|
3947
|
-
await
|
|
4025
|
+
const readme = path11.join(runtimeDir, "README.md");
|
|
4026
|
+
if (!existsSync11(readme)) {
|
|
4027
|
+
await writeFile7(readme, RUNTIME_README_BODY, "utf8");
|
|
3948
4028
|
}
|
|
3949
4029
|
}
|
|
3950
4030
|
async function ensureAiCacheLayout(cacheDir) {
|
|
3951
|
-
await
|
|
3952
|
-
const gi =
|
|
3953
|
-
if (!
|
|
3954
|
-
await
|
|
4031
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
4032
|
+
const gi = path11.join(cacheDir, ".gitignore");
|
|
4033
|
+
if (!existsSync11(gi)) {
|
|
4034
|
+
await writeFile7(gi, "*\n!.gitignore\n", "utf8");
|
|
3955
4035
|
}
|
|
3956
4036
|
}
|
|
3957
4037
|
async function ensureGitignoreEntries(root, patterns) {
|
|
3958
4038
|
try {
|
|
3959
|
-
const gitignorePath =
|
|
4039
|
+
const gitignorePath = path11.join(root, ".gitignore");
|
|
3960
4040
|
let existing = "";
|
|
3961
|
-
if (
|
|
3962
|
-
existing = await
|
|
4041
|
+
if (existsSync11(gitignorePath)) {
|
|
4042
|
+
existing = await readFile6(gitignorePath, "utf8");
|
|
3963
4043
|
}
|
|
3964
4044
|
const lines = existing.split("\n");
|
|
3965
4045
|
const missing = patterns.filter((p) => !lines.some((l) => l.trim() === p));
|
|
3966
4046
|
if (missing.length === 0) return;
|
|
3967
4047
|
const toAppend = (existing.endsWith("\n") || existing === "" ? "" : "\n") + "# hAIve project-level MCP configs (machine-specific absolute paths)\n" + missing.join("\n") + "\n";
|
|
3968
|
-
await
|
|
4048
|
+
await writeFile7(gitignorePath, existing + toAppend, "utf8");
|
|
3969
4049
|
} catch {
|
|
3970
4050
|
}
|
|
3971
4051
|
}
|
|
3972
4052
|
|
|
3973
4053
|
// src/commands/install-hooks.ts
|
|
3974
|
-
import { mkdir as
|
|
3975
|
-
import { existsSync as
|
|
3976
|
-
import
|
|
4054
|
+
import { mkdir as mkdir8, writeFile as writeFile9, chmod, readFile as readFile8 } from "fs/promises";
|
|
4055
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4056
|
+
import path13 from "path";
|
|
3977
4057
|
import "commander";
|
|
3978
4058
|
import { findProjectRoot as findProjectRoot8 } from "@hiveai/core";
|
|
3979
4059
|
|
|
3980
4060
|
// src/utils/claude-hooks.ts
|
|
3981
|
-
import { existsSync as
|
|
3982
|
-
import { mkdir as
|
|
3983
|
-
import
|
|
4061
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4062
|
+
import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
4063
|
+
import path12 from "path";
|
|
3984
4064
|
var HAIVE_HOOK_TAG = "haive-enforcement";
|
|
3985
4065
|
var POST_TOOL_USE_GROUP = {
|
|
3986
4066
|
matcher: "Edit|Write|Bash",
|
|
@@ -4066,9 +4146,9 @@ function unpatchClaudeSettings(input) {
|
|
|
4066
4146
|
async function installClaudeHooksAtPath(settingsPath) {
|
|
4067
4147
|
let raw = null;
|
|
4068
4148
|
let created = false;
|
|
4069
|
-
if (
|
|
4149
|
+
if (existsSync12(settingsPath)) {
|
|
4070
4150
|
try {
|
|
4071
|
-
raw = JSON.parse(await
|
|
4151
|
+
raw = JSON.parse(await readFile7(settingsPath, "utf8"));
|
|
4072
4152
|
} catch {
|
|
4073
4153
|
throw new Error(`${settingsPath} exists but is not valid JSON. Fix it manually first.`);
|
|
4074
4154
|
}
|
|
@@ -4076,25 +4156,25 @@ async function installClaudeHooksAtPath(settingsPath) {
|
|
|
4076
4156
|
created = true;
|
|
4077
4157
|
}
|
|
4078
4158
|
const patched = patchClaudeSettings(raw);
|
|
4079
|
-
await
|
|
4080
|
-
await
|
|
4159
|
+
await mkdir7(path12.dirname(settingsPath), { recursive: true });
|
|
4160
|
+
await writeFile8(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
|
|
4081
4161
|
return { settingsPath, created };
|
|
4082
4162
|
}
|
|
4083
4163
|
async function uninstallClaudeHooksAtPath(settingsPath) {
|
|
4084
|
-
if (!
|
|
4164
|
+
if (!existsSync12(settingsPath)) {
|
|
4085
4165
|
return { settingsPath, created: false };
|
|
4086
4166
|
}
|
|
4087
|
-
const raw = JSON.parse(await
|
|
4167
|
+
const raw = JSON.parse(await readFile7(settingsPath, "utf8"));
|
|
4088
4168
|
const cleaned = unpatchClaudeSettings(raw);
|
|
4089
|
-
await
|
|
4169
|
+
await writeFile8(settingsPath, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
|
|
4090
4170
|
return { settingsPath, created: false };
|
|
4091
4171
|
}
|
|
4092
4172
|
function defaultClaudeSettingsPath(scope, projectRoot) {
|
|
4093
4173
|
if (scope === "user") {
|
|
4094
4174
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4095
|
-
return
|
|
4175
|
+
return path12.join(home, ".claude", "settings.json");
|
|
4096
4176
|
}
|
|
4097
|
-
return
|
|
4177
|
+
return path12.join(projectRoot, ".claude", "settings.local.json");
|
|
4098
4178
|
}
|
|
4099
4179
|
|
|
4100
4180
|
// src/commands/install-hooks.ts
|
|
@@ -4151,27 +4231,27 @@ fi
|
|
|
4151
4231
|
];
|
|
4152
4232
|
async function installGitHooks(opts) {
|
|
4153
4233
|
const root = findProjectRoot8(opts.dir);
|
|
4154
|
-
const gitDir =
|
|
4155
|
-
if (!
|
|
4234
|
+
const gitDir = path13.join(root, ".git");
|
|
4235
|
+
if (!existsSync13(gitDir)) {
|
|
4156
4236
|
ui.error(`No .git directory at ${root}.`);
|
|
4157
4237
|
process.exitCode = 1;
|
|
4158
4238
|
return;
|
|
4159
4239
|
}
|
|
4160
|
-
const hooksDir =
|
|
4161
|
-
await
|
|
4240
|
+
const hooksDir = path13.join(gitDir, "hooks");
|
|
4241
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
4162
4242
|
let installed = 0;
|
|
4163
4243
|
let skipped = 0;
|
|
4164
4244
|
for (const { name, body } of HOOKS) {
|
|
4165
|
-
const file =
|
|
4166
|
-
if (
|
|
4167
|
-
const existing = await
|
|
4245
|
+
const file = path13.join(hooksDir, name);
|
|
4246
|
+
if (existsSync13(file) && !opts.force) {
|
|
4247
|
+
const existing = await readFile8(file, "utf8");
|
|
4168
4248
|
if (!existing.includes(HOOK_MARKER)) {
|
|
4169
4249
|
ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
|
|
4170
4250
|
skipped++;
|
|
4171
4251
|
continue;
|
|
4172
4252
|
}
|
|
4173
4253
|
}
|
|
4174
|
-
await
|
|
4254
|
+
await writeFile9(file, body, "utf8");
|
|
4175
4255
|
await chmod(file, 493);
|
|
4176
4256
|
installed++;
|
|
4177
4257
|
}
|
|
@@ -4226,9 +4306,9 @@ function registerInstallHooks(program2) {
|
|
|
4226
4306
|
}
|
|
4227
4307
|
|
|
4228
4308
|
// src/commands/observe.ts
|
|
4229
|
-
import { appendFile, mkdir as
|
|
4230
|
-
import { existsSync as
|
|
4231
|
-
import
|
|
4309
|
+
import { appendFile, mkdir as mkdir9 } from "fs/promises";
|
|
4310
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4311
|
+
import path14 from "path";
|
|
4232
4312
|
import "commander";
|
|
4233
4313
|
import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
|
|
4234
4314
|
var MAX_STDIN_BYTES = 256 * 1024;
|
|
@@ -4335,7 +4415,7 @@ function registerObserve(program2) {
|
|
|
4335
4415
|
})();
|
|
4336
4416
|
if (!root) return;
|
|
4337
4417
|
const paths = resolveHaivePaths7(root);
|
|
4338
|
-
if (!
|
|
4418
|
+
if (!existsSync14(paths.haiveDir)) return;
|
|
4339
4419
|
const failureHint = detectFailure(payload);
|
|
4340
4420
|
const observation = {
|
|
4341
4421
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4346,10 +4426,10 @@ function registerObserve(program2) {
|
|
|
4346
4426
|
files: extractFiles(payload),
|
|
4347
4427
|
...failureHint ? { failure_hint: true } : {}
|
|
4348
4428
|
};
|
|
4349
|
-
const cacheDir =
|
|
4350
|
-
await
|
|
4429
|
+
const cacheDir = path14.join(paths.haiveDir, ".cache");
|
|
4430
|
+
await mkdir9(cacheDir, { recursive: true });
|
|
4351
4431
|
await appendFile(
|
|
4352
|
-
|
|
4432
|
+
path14.join(cacheDir, "observations.jsonl"),
|
|
4353
4433
|
JSON.stringify(observation) + "\n",
|
|
4354
4434
|
"utf8"
|
|
4355
4435
|
);
|
|
@@ -4366,16 +4446,16 @@ import { findProjectRoot as findProjectRoot11 } from "@hiveai/core";
|
|
|
4366
4446
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4367
4447
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4368
4448
|
import { findProjectRoot as findProjectRoot10, resolveHaivePaths as resolveHaivePaths8 } from "@hiveai/core";
|
|
4369
|
-
import { mkdir as
|
|
4370
|
-
import { existsSync as
|
|
4371
|
-
import
|
|
4449
|
+
import { mkdir as mkdir10, writeFile as writeFile10 } from "fs/promises";
|
|
4450
|
+
import { existsSync as existsSync15 } from "fs";
|
|
4451
|
+
import path15 from "path";
|
|
4372
4452
|
import { z } from "zod";
|
|
4373
|
-
import { readFile as
|
|
4453
|
+
import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
|
|
4374
4454
|
import { existsSync as existsSync22 } from "fs";
|
|
4375
4455
|
import path22 from "path";
|
|
4376
4456
|
import { z as z2 } from "zod";
|
|
4377
4457
|
import { existsSync as existsSync32 } from "fs";
|
|
4378
|
-
import { loadMemoriesFromDir as
|
|
4458
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir5 } from "@hiveai/core";
|
|
4379
4459
|
import { z as z3 } from "zod";
|
|
4380
4460
|
import { createHash } from "crypto";
|
|
4381
4461
|
import { mkdir as mkdir22, writeFile as writeFile22 } from "fs/promises";
|
|
@@ -4416,7 +4496,7 @@ import { z as z6 } from "zod";
|
|
|
4416
4496
|
import { writeFile as writeFile42 } from "fs/promises";
|
|
4417
4497
|
import { existsSync as existsSync72 } from "fs";
|
|
4418
4498
|
import {
|
|
4419
|
-
loadMemoriesFromDir as
|
|
4499
|
+
loadMemoriesFromDir as loadMemoriesFromDir52,
|
|
4420
4500
|
loadUsageIndex as loadUsageIndex22,
|
|
4421
4501
|
recordRejection,
|
|
4422
4502
|
saveUsageIndex,
|
|
@@ -4486,7 +4566,7 @@ import {
|
|
|
4486
4566
|
} from "@hiveai/core";
|
|
4487
4567
|
import { z as z14 } from "zod";
|
|
4488
4568
|
import { mkdir as mkdir32, writeFile as writeFile82 } from "fs/promises";
|
|
4489
|
-
import { existsSync as
|
|
4569
|
+
import { existsSync as existsSync152 } from "fs";
|
|
4490
4570
|
import path52 from "path";
|
|
4491
4571
|
import {
|
|
4492
4572
|
buildFrontmatter as buildFrontmatter22,
|
|
@@ -4507,7 +4587,7 @@ import {
|
|
|
4507
4587
|
serializeMemory as serializeMemory8
|
|
4508
4588
|
} from "@hiveai/core";
|
|
4509
4589
|
import { z as z16 } from "zod";
|
|
4510
|
-
import { mkdir as mkdir52, writeFile as
|
|
4590
|
+
import { mkdir as mkdir52, writeFile as writeFile102 } from "fs/promises";
|
|
4511
4591
|
import { existsSync as existsSync17 } from "fs";
|
|
4512
4592
|
import path72 from "path";
|
|
4513
4593
|
import {
|
|
@@ -4552,7 +4632,7 @@ import {
|
|
|
4552
4632
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
4553
4633
|
isAutoPromoteEligible,
|
|
4554
4634
|
isDecaying,
|
|
4555
|
-
isRetiredMemory,
|
|
4635
|
+
isRetiredMemory as isRetiredMemory2,
|
|
4556
4636
|
literalMatchesAllTokens as literalMatchesAllTokens22,
|
|
4557
4637
|
literalMatchesAnyToken as literalMatchesAnyToken22,
|
|
4558
4638
|
loadCodeMap as loadCodeMap5,
|
|
@@ -4617,7 +4697,7 @@ import {
|
|
|
4617
4697
|
deriveConfidence as deriveConfidence6,
|
|
4618
4698
|
diffHasDistinctiveOverlap,
|
|
4619
4699
|
getUsage as getUsage8,
|
|
4620
|
-
isRetiredMemory as
|
|
4700
|
+
isRetiredMemory as isRetiredMemory22,
|
|
4621
4701
|
loadMemoriesFromDir as loadMemoriesFromDir19,
|
|
4622
4702
|
loadUsageIndex as loadUsageIndex10,
|
|
4623
4703
|
literalMatchesAnyToken as literalMatchesAnyToken3,
|
|
@@ -4703,15 +4783,15 @@ var BootstrapProjectSaveInputSchema = {
|
|
|
4703
4783
|
overwrite: z.boolean().default(false).describe("Overwrite an existing file instead of failing")
|
|
4704
4784
|
};
|
|
4705
4785
|
async function bootstrapProjectSave(input, ctx) {
|
|
4706
|
-
const target = input.module ?
|
|
4707
|
-
const exists =
|
|
4786
|
+
const target = input.module ? path15.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
|
|
4787
|
+
const exists = existsSync15(target);
|
|
4708
4788
|
if (exists && !input.overwrite) {
|
|
4709
4789
|
throw new Error(
|
|
4710
4790
|
`${target} already exists. Pass overwrite=true to replace it.`
|
|
4711
4791
|
);
|
|
4712
4792
|
}
|
|
4713
|
-
await
|
|
4714
|
-
await
|
|
4793
|
+
await mkdir10(path15.dirname(target), { recursive: true });
|
|
4794
|
+
await writeFile10(target, input.content, "utf8");
|
|
4715
4795
|
return {
|
|
4716
4796
|
file_path: target,
|
|
4717
4797
|
action: exists ? "overwritten" : "created"
|
|
@@ -4724,14 +4804,14 @@ var GetProjectContextInputSchema = {
|
|
|
4724
4804
|
async function getProjectContext(input, ctx) {
|
|
4725
4805
|
const out = { root_context: null };
|
|
4726
4806
|
if (existsSync22(ctx.paths.projectContext)) {
|
|
4727
|
-
out.root_context = await
|
|
4807
|
+
out.root_context = await readFile9(ctx.paths.projectContext, "utf8");
|
|
4728
4808
|
}
|
|
4729
4809
|
if (input.module) {
|
|
4730
4810
|
const modFile = path22.join(ctx.paths.modulesContextDir, input.module, "context.md");
|
|
4731
4811
|
if (existsSync22(modFile)) {
|
|
4732
4812
|
out.module_context = {
|
|
4733
4813
|
name: input.module,
|
|
4734
|
-
content: await
|
|
4814
|
+
content: await readFile9(modFile, "utf8")
|
|
4735
4815
|
};
|
|
4736
4816
|
}
|
|
4737
4817
|
}
|
|
@@ -4758,7 +4838,7 @@ async function memList(input, ctx) {
|
|
|
4758
4838
|
if (!existsSync32(ctx.paths.memoriesDir)) {
|
|
4759
4839
|
return { memories: [] };
|
|
4760
4840
|
}
|
|
4761
|
-
const all = await
|
|
4841
|
+
const all = await loadMemoriesFromDir5(ctx.paths.memoriesDir);
|
|
4762
4842
|
const filtered = all.filter(({ memory: memory2 }) => {
|
|
4763
4843
|
const fm = memory2.frontmatter;
|
|
4764
4844
|
if (input.scope && fm.scope !== input.scope) return false;
|
|
@@ -5252,7 +5332,7 @@ async function memReject(input, ctx) {
|
|
|
5252
5332
|
if (!existsSync72(ctx.paths.memoriesDir)) {
|
|
5253
5333
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
5254
5334
|
}
|
|
5255
|
-
const memories = await
|
|
5335
|
+
const memories = await loadMemoriesFromDir52(ctx.paths.memoriesDir);
|
|
5256
5336
|
const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
|
|
5257
5337
|
if (!loaded) throw new Error(`No memory with id "${input.id}".`);
|
|
5258
5338
|
await writeFile42(
|
|
@@ -5645,7 +5725,7 @@ var MemTriedInputSchema = {
|
|
|
5645
5725
|
author: z15.string().optional().describe("Author handle or email")
|
|
5646
5726
|
};
|
|
5647
5727
|
async function memTried(input, ctx) {
|
|
5648
|
-
if (!
|
|
5728
|
+
if (!existsSync152(ctx.paths.haiveDir)) {
|
|
5649
5729
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
5650
5730
|
}
|
|
5651
5731
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
@@ -5671,7 +5751,7 @@ async function memTried(input, ctx) {
|
|
|
5671
5751
|
}
|
|
5672
5752
|
const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
5673
5753
|
await mkdir32(path52.dirname(file), { recursive: true });
|
|
5674
|
-
if (
|
|
5754
|
+
if (existsSync152(file)) {
|
|
5675
5755
|
throw new Error(`Memory already exists at ${file}`);
|
|
5676
5756
|
}
|
|
5677
5757
|
await writeFile82(file, serializeMemory7({ frontmatter, body }), "utf8");
|
|
@@ -5805,7 +5885,7 @@ async function memObserve(input, ctx) {
|
|
|
5805
5885
|
if (existsSync17(file)) {
|
|
5806
5886
|
throw new Error(`Memory already exists at ${file}`);
|
|
5807
5887
|
}
|
|
5808
|
-
await
|
|
5888
|
+
await writeFile102(file, serializeMemory9({ frontmatter, body }), "utf8");
|
|
5809
5889
|
return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
|
|
5810
5890
|
}
|
|
5811
5891
|
function pendingDistillPath(ctx) {
|
|
@@ -6257,7 +6337,7 @@ async function getBriefing(input, ctx) {
|
|
|
6257
6337
|
const s = memory2.frontmatter.status;
|
|
6258
6338
|
if (s === "rejected" || s === "deprecated") return false;
|
|
6259
6339
|
if (!input.include_stale && s === "stale") return false;
|
|
6260
|
-
if (!input.include_stale &&
|
|
6340
|
+
if (!input.include_stale && isRetiredMemory2(memory2.frontmatter, memory2.body)) return false;
|
|
6261
6341
|
if (memory2.frontmatter.type === "session_recap") return false;
|
|
6262
6342
|
return true;
|
|
6263
6343
|
});
|
|
@@ -7125,7 +7205,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
7125
7205
|
const t = memory2.frontmatter.type;
|
|
7126
7206
|
if (t !== "attempt" && t !== "gotcha") return false;
|
|
7127
7207
|
const s = memory2.frontmatter.status;
|
|
7128
|
-
return s !== "rejected" && s !== "deprecated" && s !== "stale" && !
|
|
7208
|
+
return s !== "rejected" && s !== "deprecated" && s !== "stale" && !isRetiredMemory22(memory2.frontmatter, memory2.body);
|
|
7129
7209
|
});
|
|
7130
7210
|
if (negative.length === 0) {
|
|
7131
7211
|
return { scanned: 0, warnings: [], notice: "No attempt/gotcha memories found yet." };
|
|
@@ -8491,7 +8571,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
8491
8571
|
};
|
|
8492
8572
|
}
|
|
8493
8573
|
var SERVER_NAME = "haive";
|
|
8494
|
-
var SERVER_VERSION = "0.
|
|
8574
|
+
var SERVER_VERSION = "0.19.0";
|
|
8495
8575
|
function jsonResult(data) {
|
|
8496
8576
|
return {
|
|
8497
8577
|
content: [
|
|
@@ -9523,8 +9603,8 @@ function registerMcp(program2) {
|
|
|
9523
9603
|
|
|
9524
9604
|
// src/commands/sync.ts
|
|
9525
9605
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
9526
|
-
import { readFile as readFile10, writeFile as
|
|
9527
|
-
import { existsSync as
|
|
9606
|
+
import { readFile as readFile10, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
|
|
9607
|
+
import { existsSync as existsSync33 } from "fs";
|
|
9528
9608
|
import path16 from "path";
|
|
9529
9609
|
import "commander";
|
|
9530
9610
|
import {
|
|
@@ -9537,7 +9617,7 @@ import {
|
|
|
9537
9617
|
isStackPackSeed,
|
|
9538
9618
|
loadCodeMap as loadCodeMap6,
|
|
9539
9619
|
loadConfig as loadConfig4,
|
|
9540
|
-
loadMemoriesFromDir as
|
|
9620
|
+
loadMemoriesFromDir as loadMemoriesFromDir25,
|
|
9541
9621
|
loadUsageIndex as loadUsageIndex13,
|
|
9542
9622
|
pullCrossRepoSources,
|
|
9543
9623
|
resolveHaivePaths as resolveHaivePaths9,
|
|
@@ -9547,90 +9627,7 @@ import {
|
|
|
9547
9627
|
verifyAnchor as verifyAnchor2,
|
|
9548
9628
|
watchContracts
|
|
9549
9629
|
} from "@hiveai/core";
|
|
9550
|
-
import { BRIDGE_TARGETS } from "@hiveai/core";
|
|
9551
|
-
|
|
9552
|
-
// src/utils/bridge-files.ts
|
|
9553
|
-
import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile15 } from "fs/promises";
|
|
9554
|
-
import { existsSync as existsSync33 } from "fs";
|
|
9555
|
-
import path15 from "path";
|
|
9556
|
-
import {
|
|
9557
|
-
BRIDGE_MARKERS,
|
|
9558
|
-
generateBridges,
|
|
9559
|
-
isRetiredMemory as isRetiredMemory3,
|
|
9560
|
-
loadMemoriesFromDir as loadMemoriesFromDir25
|
|
9561
|
-
} from "@hiveai/core";
|
|
9562
|
-
async function writeBridgeFiles(root, paths, opts) {
|
|
9563
|
-
const result = { created: [], updated: [], unchanged: [] };
|
|
9564
|
-
if (!existsSync33(paths.memoriesDir)) return result;
|
|
9565
|
-
const allLoaded = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
9566
|
-
const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory3(m.frontmatter, m.body));
|
|
9567
|
-
const sensors = [];
|
|
9568
|
-
for (const m of memories) {
|
|
9569
|
-
const sensor = m.frontmatter.sensor;
|
|
9570
|
-
if (!sensor || sensor.severity !== "block") continue;
|
|
9571
|
-
sensors.push({
|
|
9572
|
-
id: m.frontmatter.id,
|
|
9573
|
-
severity: "block",
|
|
9574
|
-
message: sensor.message,
|
|
9575
|
-
...sensor.pattern ? { pattern: sensor.pattern } : {},
|
|
9576
|
-
paths: sensor.paths.length > 0 ? sensor.paths : m.frontmatter.anchor.paths
|
|
9577
|
-
});
|
|
9578
|
-
}
|
|
9579
|
-
const maxMemories = Math.max(1, opts.maxMemories ?? 8);
|
|
9580
|
-
const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
|
|
9581
|
-
for (const output of outputs) {
|
|
9582
|
-
const targetFile = path15.join(root, output.path);
|
|
9583
|
-
const fileExists = existsSync33(targetFile);
|
|
9584
|
-
if (opts.onlyExisting && !fileExists) continue;
|
|
9585
|
-
if (opts.dryRun) {
|
|
9586
|
-
(fileExists ? result.updated : result.created).push(output.path);
|
|
9587
|
-
continue;
|
|
9588
|
-
}
|
|
9589
|
-
await mkdir10(path15.dirname(targetFile), { recursive: true });
|
|
9590
|
-
if (!fileExists) {
|
|
9591
|
-
await writeFile15(targetFile, output.content, "utf8");
|
|
9592
|
-
result.created.push(output.path);
|
|
9593
|
-
continue;
|
|
9594
|
-
}
|
|
9595
|
-
let existing = (await readFile9(targetFile, "utf8")).replace(/\r\n/g, "\n");
|
|
9596
|
-
const withMemories = replaceMarkerBlock(
|
|
9597
|
-
existing,
|
|
9598
|
-
BRIDGE_MARKERS.memoriesStart,
|
|
9599
|
-
BRIDGE_MARKERS.memoriesEnd,
|
|
9600
|
-
extractMarkerBlock(output.content, BRIDGE_MARKERS.memoriesStart, BRIDGE_MARKERS.memoriesEnd)
|
|
9601
|
-
);
|
|
9602
|
-
const sensorsBlockContent = extractMarkerBlock(
|
|
9603
|
-
output.content,
|
|
9604
|
-
BRIDGE_MARKERS.sensorsStart,
|
|
9605
|
-
BRIDGE_MARKERS.sensorsEnd
|
|
9606
|
-
);
|
|
9607
|
-
const withSensors = sensorsBlockContent ? replaceMarkerBlock(withMemories, BRIDGE_MARKERS.sensorsStart, BRIDGE_MARKERS.sensorsEnd, sensorsBlockContent) : withMemories;
|
|
9608
|
-
if (withSensors === existing) {
|
|
9609
|
-
result.unchanged.push(output.path);
|
|
9610
|
-
continue;
|
|
9611
|
-
}
|
|
9612
|
-
await writeFile15(targetFile, withSensors, "utf8");
|
|
9613
|
-
result.updated.push(output.path);
|
|
9614
|
-
}
|
|
9615
|
-
return result;
|
|
9616
|
-
}
|
|
9617
|
-
function extractMarkerBlock(text, startMarker, endMarker) {
|
|
9618
|
-
const startIdx = text.indexOf(startMarker);
|
|
9619
|
-
const endIdx = text.indexOf(endMarker);
|
|
9620
|
-
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return null;
|
|
9621
|
-
return text.slice(startIdx, endIdx + endMarker.length);
|
|
9622
|
-
}
|
|
9623
|
-
function replaceMarkerBlock(existing, startMarker, endMarker, replacement) {
|
|
9624
|
-
if (!replacement) return existing;
|
|
9625
|
-
const startIdx = existing.indexOf(startMarker);
|
|
9626
|
-
const endIdx = existing.indexOf(endMarker);
|
|
9627
|
-
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
9628
|
-
return existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + replacement + "\n";
|
|
9629
|
-
}
|
|
9630
|
-
return existing.slice(0, startIdx) + replacement + existing.slice(endIdx + endMarker.length);
|
|
9631
|
-
}
|
|
9632
|
-
|
|
9633
|
-
// src/commands/sync.ts
|
|
9630
|
+
import { BRIDGE_TARGETS as BRIDGE_TARGETS2 } from "@hiveai/core";
|
|
9634
9631
|
var BRIDGE_START = "<!-- haive:memories-start -->";
|
|
9635
9632
|
var BRIDGE_END = "<!-- haive:memories-end -->";
|
|
9636
9633
|
function registerSync(program2) {
|
|
@@ -9645,7 +9642,7 @@ function registerSync(program2) {
|
|
|
9645
9642
|
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").option("--no-bridges", "skip auto-refresh of existing native agent bridge files (.cursor/rules, .clinerules, \u2026)").option("--dry-run", "report what would change without writing any files").action(async (opts) => {
|
|
9646
9643
|
const root = findProjectRoot12(opts.dir);
|
|
9647
9644
|
const paths = resolveHaivePaths9(root);
|
|
9648
|
-
if (!
|
|
9645
|
+
if (!existsSync33(paths.memoriesDir)) {
|
|
9649
9646
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
9650
9647
|
process.exitCode = 1;
|
|
9651
9648
|
return;
|
|
@@ -9664,12 +9661,12 @@ function registerSync(program2) {
|
|
|
9664
9661
|
let promoted = 0;
|
|
9665
9662
|
let autoApproved = 0;
|
|
9666
9663
|
if (opts.verify !== false) {
|
|
9667
|
-
const memories = await
|
|
9664
|
+
const memories = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
9668
9665
|
for (const { memory: memory2, filePath } of memories) {
|
|
9669
9666
|
if (memory2.frontmatter.type === "session_recap") {
|
|
9670
9667
|
if (memory2.frontmatter.status === "stale") {
|
|
9671
9668
|
if (!dryRun) {
|
|
9672
|
-
await
|
|
9669
|
+
await writeFile15(
|
|
9673
9670
|
filePath,
|
|
9674
9671
|
serializeMemory13({
|
|
9675
9672
|
frontmatter: {
|
|
@@ -9694,7 +9691,7 @@ function registerSync(program2) {
|
|
|
9694
9691
|
if (result.stale) {
|
|
9695
9692
|
if (memory2.frontmatter.status !== "stale") {
|
|
9696
9693
|
if (!dryRun) {
|
|
9697
|
-
await
|
|
9694
|
+
await writeFile15(
|
|
9698
9695
|
filePath,
|
|
9699
9696
|
serializeMemory13({
|
|
9700
9697
|
frontmatter: {
|
|
@@ -9712,7 +9709,7 @@ function registerSync(program2) {
|
|
|
9712
9709
|
}
|
|
9713
9710
|
} else if (memory2.frontmatter.status === "stale") {
|
|
9714
9711
|
if (!dryRun) {
|
|
9715
|
-
await
|
|
9712
|
+
await writeFile15(
|
|
9716
9713
|
filePath,
|
|
9717
9714
|
serializeMemory13({
|
|
9718
9715
|
frontmatter: {
|
|
@@ -9731,7 +9728,7 @@ function registerSync(program2) {
|
|
|
9731
9728
|
}
|
|
9732
9729
|
}
|
|
9733
9730
|
if (opts.promote !== false) {
|
|
9734
|
-
const memories = await
|
|
9731
|
+
const memories = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
9735
9732
|
const usage = await loadUsageIndex13(paths);
|
|
9736
9733
|
const nowMs = Date.now();
|
|
9737
9734
|
for (const { memory: memory2, filePath } of memories) {
|
|
@@ -9742,7 +9739,7 @@ function registerSync(program2) {
|
|
|
9742
9739
|
maxRejections: DEFAULT_AUTO_PROMOTE_RULE2.maxRejections
|
|
9743
9740
|
})) {
|
|
9744
9741
|
if (!dryRun) {
|
|
9745
|
-
await
|
|
9742
|
+
await writeFile15(
|
|
9746
9743
|
filePath,
|
|
9747
9744
|
serializeMemory13({ frontmatter: { ...fm, status: "validated" }, body: memory2.body }),
|
|
9748
9745
|
"utf8"
|
|
@@ -9755,7 +9752,7 @@ function registerSync(program2) {
|
|
|
9755
9752
|
const ageHours = (nowMs - new Date(fm.created_at).getTime()) / (1e3 * 60 * 60);
|
|
9756
9753
|
if (ageHours >= autoApproveDelayHours) {
|
|
9757
9754
|
if (!dryRun) {
|
|
9758
|
-
await
|
|
9755
|
+
await writeFile15(
|
|
9759
9756
|
filePath,
|
|
9760
9757
|
serializeMemory13({
|
|
9761
9758
|
frontmatter: {
|
|
@@ -9783,7 +9780,7 @@ function registerSync(program2) {
|
|
|
9783
9780
|
for (const repair of repairs) log(ui.dim(`autopilot: ${repair.message}`));
|
|
9784
9781
|
}
|
|
9785
9782
|
const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
|
|
9786
|
-
const draftMemories = (await
|
|
9783
|
+
const draftMemories = (await loadMemoriesFromDir25(paths.memoriesDir)).filter(
|
|
9787
9784
|
(m) => m.memory.frontmatter.status === "draft"
|
|
9788
9785
|
);
|
|
9789
9786
|
const draftCount = draftMemories.length;
|
|
@@ -9806,14 +9803,14 @@ function registerSync(program2) {
|
|
|
9806
9803
|
} else {
|
|
9807
9804
|
const agentsMd = path16.join(root, "AGENTS.md");
|
|
9808
9805
|
bridgeTargets = [path16.join(root, "CLAUDE.md")];
|
|
9809
|
-
if (
|
|
9806
|
+
if (existsSync33(agentsMd)) bridgeTargets.push(agentsMd);
|
|
9810
9807
|
}
|
|
9811
9808
|
for (const bridgeFile of bridgeTargets) {
|
|
9812
9809
|
await injectBridge(bridgeFile, paths.memoriesDir, maxInject, root, opts.quiet);
|
|
9813
9810
|
}
|
|
9814
9811
|
}
|
|
9815
9812
|
if (opts.noBridges !== true) {
|
|
9816
|
-
const nativeTargets =
|
|
9813
|
+
const nativeTargets = BRIDGE_TARGETS2.filter(
|
|
9817
9814
|
(t) => t !== "claude" && t !== "agents"
|
|
9818
9815
|
);
|
|
9819
9816
|
try {
|
|
@@ -9849,7 +9846,7 @@ function registerSync(program2) {
|
|
|
9849
9846
|
}
|
|
9850
9847
|
}
|
|
9851
9848
|
if (!opts.quiet) {
|
|
9852
|
-
const allForDecay = await
|
|
9849
|
+
const allForDecay = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
9853
9850
|
const usageForDecay = await loadUsageIndex13(paths);
|
|
9854
9851
|
const decaying = allForDecay.filter(({ memory: memory2 }) => {
|
|
9855
9852
|
const fm = memory2.frontmatter;
|
|
@@ -9939,7 +9936,7 @@ Wait for **explicit confirmation** before acting.
|
|
|
9939
9936
|
if (!dryRun) {
|
|
9940
9937
|
const teamDir = path16.join(paths.memoriesDir, "team");
|
|
9941
9938
|
await mkdir11(teamDir, { recursive: true });
|
|
9942
|
-
await
|
|
9939
|
+
await writeFile15(
|
|
9943
9940
|
path16.join(teamDir, `${fm.id}.md`),
|
|
9944
9941
|
serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
9945
9942
|
"utf8"
|
|
@@ -10008,7 +10005,7 @@ Wait for **explicit confirmation** before acting.
|
|
|
10008
10005
|
if (!dryRun) {
|
|
10009
10006
|
const teamDir = path16.join(paths.memoriesDir, "team");
|
|
10010
10007
|
await mkdir11(teamDir, { recursive: true });
|
|
10011
|
-
await
|
|
10008
|
+
await writeFile15(
|
|
10012
10009
|
path16.join(teamDir, `${fm.id}.md`),
|
|
10013
10010
|
serializeMemory13({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
10014
10011
|
"utf8"
|
|
@@ -10100,8 +10097,8 @@ function bridgeSummaryLine(body) {
|
|
|
10100
10097
|
return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
|
|
10101
10098
|
}
|
|
10102
10099
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
10103
|
-
if (!
|
|
10104
|
-
const all = await
|
|
10100
|
+
if (!existsSync33(memoriesDir)) return;
|
|
10101
|
+
const all = await loadMemoriesFromDir25(memoriesDir);
|
|
10105
10102
|
const top = all.filter(({ memory: memory2 }) => {
|
|
10106
10103
|
const s = memory2.frontmatter.status;
|
|
10107
10104
|
if (memory2.frontmatter.type === "session_recap") return false;
|
|
@@ -10126,7 +10123,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
|
10126
10123
|
` + block + `
|
|
10127
10124
|
|
|
10128
10125
|
${BRIDGE_END}`;
|
|
10129
|
-
const fileExists =
|
|
10126
|
+
const fileExists = existsSync33(bridgeFile);
|
|
10130
10127
|
let existing = fileExists ? await readFile10(bridgeFile, "utf8") : "";
|
|
10131
10128
|
existing = existing.replace(/\r\n/g, "\n");
|
|
10132
10129
|
const startIdx = existing.indexOf(BRIDGE_START);
|
|
@@ -10148,7 +10145,7 @@ ${BRIDGE_END}`;
|
|
|
10148
10145
|
}
|
|
10149
10146
|
updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
|
|
10150
10147
|
}
|
|
10151
|
-
await
|
|
10148
|
+
await writeFile15(bridgeFile, updated, "utf8");
|
|
10152
10149
|
if (!quiet) {
|
|
10153
10150
|
console.log(
|
|
10154
10151
|
ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path16.relative(root, bridgeFile)}`)
|
|
@@ -10176,8 +10173,8 @@ function collectSinceChanges(root, ref) {
|
|
|
10176
10173
|
|
|
10177
10174
|
// src/commands/memory-add.ts
|
|
10178
10175
|
import { createHash as createHash2 } from "crypto";
|
|
10179
|
-
import { mkdir as mkdir12, readFile as readFile11, writeFile as
|
|
10180
|
-
import { existsSync as
|
|
10176
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile16 } from "fs/promises";
|
|
10177
|
+
import { existsSync as existsSync34 } from "fs";
|
|
10181
10178
|
import path17 from "path";
|
|
10182
10179
|
import "commander";
|
|
10183
10180
|
import {
|
|
@@ -10185,7 +10182,7 @@ import {
|
|
|
10185
10182
|
findProjectRoot as findProjectRoot13,
|
|
10186
10183
|
inferModulesFromPaths as inferModulesFromPaths3,
|
|
10187
10184
|
loadConfig as loadConfig5,
|
|
10188
|
-
loadMemoriesFromDir as
|
|
10185
|
+
loadMemoriesFromDir as loadMemoriesFromDir26,
|
|
10189
10186
|
memoryFilePath as memoryFilePath7,
|
|
10190
10187
|
resolveHaivePaths as resolveHaivePaths10,
|
|
10191
10188
|
serializeMemory as serializeMemory14,
|
|
@@ -10219,7 +10216,7 @@ function registerMemoryAdd(memory2) {
|
|
|
10219
10216
|
).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("--activation-keyword <csv>", "skill only: comma-separated keywords that trigger progressive disclosure of this skill").option("--activation-glob <csv>", "skill only: comma-separated path globs that trigger this skill").option("--activation-always", "skill only: always surface this skill (no triggers needed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10220
10217
|
const root = findProjectRoot13(opts.dir);
|
|
10221
10218
|
const paths = resolveHaivePaths10(root);
|
|
10222
|
-
if (!
|
|
10219
|
+
if (!existsSync34(paths.haiveDir)) {
|
|
10223
10220
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
10224
10221
|
process.exitCode = 1;
|
|
10225
10222
|
return;
|
|
@@ -10236,7 +10233,7 @@ function registerMemoryAdd(memory2) {
|
|
|
10236
10233
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
|
|
10237
10234
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
10238
10235
|
if (anchorPaths.length > 0) {
|
|
10239
|
-
const missing = anchorPaths.filter((p) => !
|
|
10236
|
+
const missing = anchorPaths.filter((p) => !existsSync34(path17.resolve(root, p)));
|
|
10240
10237
|
if (missing.length > 0) {
|
|
10241
10238
|
ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
|
|
10242
10239
|
for (const p of missing) ui.warn(` \u2717 ${p}`);
|
|
@@ -10249,7 +10246,7 @@ function registerMemoryAdd(memory2) {
|
|
|
10249
10246
|
const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
|
|
10250
10247
|
let body;
|
|
10251
10248
|
if (opts.bodyFile !== void 0) {
|
|
10252
|
-
if (!
|
|
10249
|
+
if (!existsSync34(opts.bodyFile)) {
|
|
10253
10250
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
10254
10251
|
process.exitCode = 1;
|
|
10255
10252
|
return;
|
|
@@ -10265,9 +10262,9 @@ TODO \u2014 write the memory body.
|
|
|
10265
10262
|
`;
|
|
10266
10263
|
}
|
|
10267
10264
|
const scope = opts.scope ?? config.defaultScope ?? "personal";
|
|
10268
|
-
if (
|
|
10265
|
+
if (existsSync34(paths.memoriesDir)) {
|
|
10269
10266
|
const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
10270
|
-
const allForHash = await
|
|
10267
|
+
const allForHash = await loadMemoriesFromDir26(paths.memoriesDir);
|
|
10271
10268
|
const hashDup = allForHash.find(
|
|
10272
10269
|
({ memory: memory3 }) => createHash2("sha256").update(memory3.body.trim()).digest("hex").slice(0, 12) === incomingHash && memory3.frontmatter.scope === scope
|
|
10273
10270
|
);
|
|
@@ -10278,8 +10275,8 @@ TODO \u2014 write the memory body.
|
|
|
10278
10275
|
return;
|
|
10279
10276
|
}
|
|
10280
10277
|
}
|
|
10281
|
-
if (opts.topic &&
|
|
10282
|
-
const existing = await
|
|
10278
|
+
if (opts.topic && existsSync34(paths.memoriesDir)) {
|
|
10279
|
+
const existing = await loadMemoriesFromDir26(paths.memoriesDir);
|
|
10283
10280
|
const topicMatch = existing.find(
|
|
10284
10281
|
({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
|
|
10285
10282
|
);
|
|
@@ -10299,7 +10296,7 @@ TODO \u2014 write the memory body.
|
|
|
10299
10296
|
};
|
|
10300
10297
|
const suggestedSensor = !newFrontmatter.sensor ? suggestSensorForCliMemory(opts.type, body, newFrontmatter.anchor.paths) : null;
|
|
10301
10298
|
if (suggestedSensor) newFrontmatter.sensor = suggestedSensor;
|
|
10302
|
-
await
|
|
10299
|
+
await writeFile16(topicMatch.filePath, serializeMemory14({ frontmatter: newFrontmatter, body }), "utf8");
|
|
10303
10300
|
ui.success(`Updated (topic upsert) ${path17.relative(root, topicMatch.filePath)}`);
|
|
10304
10301
|
ui.info(`id=${fm.id} revision=${revisionCount}`);
|
|
10305
10302
|
if (suggestedSensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(suggestedSensor.pattern)}`);
|
|
@@ -10325,13 +10322,13 @@ TODO \u2014 write the memory body.
|
|
|
10325
10322
|
});
|
|
10326
10323
|
const file = memoryFilePath7(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
10327
10324
|
await mkdir12(path17.dirname(file), { recursive: true });
|
|
10328
|
-
if (
|
|
10325
|
+
if (existsSync34(file)) {
|
|
10329
10326
|
ui.error(`Memory already exists at ${file}`);
|
|
10330
10327
|
process.exitCode = 1;
|
|
10331
10328
|
return;
|
|
10332
10329
|
}
|
|
10333
|
-
if (
|
|
10334
|
-
const existing = await
|
|
10330
|
+
if (existsSync34(paths.memoriesDir)) {
|
|
10331
|
+
const existing = await loadMemoriesFromDir26(paths.memoriesDir);
|
|
10335
10332
|
const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
10336
10333
|
const similar = existing.filter(({ memory: memory3 }) => {
|
|
10337
10334
|
const id = memory3.frontmatter.id.toLowerCase();
|
|
@@ -10342,7 +10339,7 @@ TODO \u2014 write the memory body.
|
|
|
10342
10339
|
ui.warn("Consider updating one of these with `haive memory update` instead.");
|
|
10343
10340
|
}
|
|
10344
10341
|
}
|
|
10345
|
-
await
|
|
10342
|
+
await writeFile16(file, serializeMemory14({ frontmatter, body }), "utf8");
|
|
10346
10343
|
ui.success(`Created ${path17.relative(root, file)}`);
|
|
10347
10344
|
ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
|
|
10348
10345
|
if (frontmatter.sensor?.autogen) {
|
|
@@ -10422,14 +10419,14 @@ function slugify(value) {
|
|
|
10422
10419
|
}
|
|
10423
10420
|
|
|
10424
10421
|
// src/commands/memory-list.ts
|
|
10425
|
-
import { existsSync as
|
|
10422
|
+
import { existsSync as existsSync35 } from "fs";
|
|
10426
10423
|
import path18 from "path";
|
|
10427
10424
|
import "commander";
|
|
10428
10425
|
import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
|
|
10429
10426
|
|
|
10430
10427
|
// src/utils/fs.ts
|
|
10431
10428
|
import {
|
|
10432
|
-
loadMemoriesFromDir as
|
|
10429
|
+
loadMemoriesFromDir as loadMemoriesFromDir27,
|
|
10433
10430
|
loadMemory,
|
|
10434
10431
|
listMarkdownFilesRecursive
|
|
10435
10432
|
} from "@hiveai/core";
|
|
@@ -10439,12 +10436,12 @@ function registerMemoryList(memory2) {
|
|
|
10439
10436
|
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("--limit <n>", "max memories to display").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10440
10437
|
const root = findProjectRoot14(opts.dir);
|
|
10441
10438
|
const paths = resolveHaivePaths11(root);
|
|
10442
|
-
if (!
|
|
10439
|
+
if (!existsSync35(paths.memoriesDir)) {
|
|
10443
10440
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
10444
10441
|
process.exitCode = 1;
|
|
10445
10442
|
return;
|
|
10446
10443
|
}
|
|
10447
|
-
const all = await
|
|
10444
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10448
10445
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
10449
10446
|
const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10)) : void 0;
|
|
10450
10447
|
const filtered = all.filter((m) => {
|
|
@@ -10513,8 +10510,8 @@ function matchesFilters(loaded, opts) {
|
|
|
10513
10510
|
}
|
|
10514
10511
|
|
|
10515
10512
|
// src/commands/memory-promote.ts
|
|
10516
|
-
import { mkdir as mkdir13, unlink as unlink2, writeFile as
|
|
10517
|
-
import { existsSync as
|
|
10513
|
+
import { mkdir as mkdir13, unlink as unlink2, writeFile as writeFile17 } from "fs/promises";
|
|
10514
|
+
import { existsSync as existsSync36 } from "fs";
|
|
10518
10515
|
import path19 from "path";
|
|
10519
10516
|
import "commander";
|
|
10520
10517
|
import {
|
|
@@ -10527,12 +10524,12 @@ function registerMemoryPromote(memory2) {
|
|
|
10527
10524
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10528
10525
|
const root = findProjectRoot15(opts.dir);
|
|
10529
10526
|
const paths = resolveHaivePaths12(root);
|
|
10530
|
-
if (!
|
|
10527
|
+
if (!existsSync36(paths.memoriesDir)) {
|
|
10531
10528
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
10532
10529
|
process.exitCode = 1;
|
|
10533
10530
|
return;
|
|
10534
10531
|
}
|
|
10535
|
-
const teamAndModule = await
|
|
10532
|
+
const teamAndModule = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10536
10533
|
const alreadyShared = teamAndModule.find(
|
|
10537
10534
|
(m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
|
|
10538
10535
|
);
|
|
@@ -10546,7 +10543,7 @@ function registerMemoryPromote(memory2) {
|
|
|
10546
10543
|
}
|
|
10547
10544
|
return;
|
|
10548
10545
|
}
|
|
10549
|
-
const all = await
|
|
10546
|
+
const all = await loadMemoriesFromDir27(paths.personalDir);
|
|
10550
10547
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
10551
10548
|
if (!found) {
|
|
10552
10549
|
ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
|
|
@@ -10563,7 +10560,7 @@ function registerMemoryPromote(memory2) {
|
|
|
10563
10560
|
};
|
|
10564
10561
|
const newPath = memoryFilePath8(paths, "team", updated.frontmatter.id);
|
|
10565
10562
|
await mkdir13(path19.dirname(newPath), { recursive: true });
|
|
10566
|
-
await
|
|
10563
|
+
await writeFile17(newPath, serializeMemory15(updated), "utf8");
|
|
10567
10564
|
await unlink2(found.filePath);
|
|
10568
10565
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
10569
10566
|
ui.info(`Now at ${path19.relative(root, newPath)}`);
|
|
@@ -10572,8 +10569,8 @@ function registerMemoryPromote(memory2) {
|
|
|
10572
10569
|
}
|
|
10573
10570
|
|
|
10574
10571
|
// src/commands/memory-approve.ts
|
|
10575
|
-
import { existsSync as
|
|
10576
|
-
import { writeFile as
|
|
10572
|
+
import { existsSync as existsSync37 } from "fs";
|
|
10573
|
+
import { writeFile as writeFile18 } from "fs/promises";
|
|
10577
10574
|
import path20 from "path";
|
|
10578
10575
|
import "commander";
|
|
10579
10576
|
import {
|
|
@@ -10585,12 +10582,12 @@ function registerMemoryApprove(memory2) {
|
|
|
10585
10582
|
memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10586
10583
|
const root = findProjectRoot16(opts.dir);
|
|
10587
10584
|
const paths = resolveHaivePaths13(root);
|
|
10588
|
-
if (!
|
|
10585
|
+
if (!existsSync37(paths.memoriesDir)) {
|
|
10589
10586
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10590
10587
|
process.exitCode = 1;
|
|
10591
10588
|
return;
|
|
10592
10589
|
}
|
|
10593
|
-
const all = await
|
|
10590
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10594
10591
|
if (opts.all || opts.pending) {
|
|
10595
10592
|
const candidates = all.filter((m) => {
|
|
10596
10593
|
const s = m.memory.frontmatter.status;
|
|
@@ -10607,7 +10604,7 @@ function registerMemoryApprove(memory2) {
|
|
|
10607
10604
|
frontmatter: { ...found2.memory.frontmatter, status: "validated" },
|
|
10608
10605
|
body: found2.memory.body
|
|
10609
10606
|
};
|
|
10610
|
-
await
|
|
10607
|
+
await writeFile18(found2.filePath, serializeMemory16(next2), "utf8");
|
|
10611
10608
|
count++;
|
|
10612
10609
|
}
|
|
10613
10610
|
ui.success(`Approved ${count} memor${count === 1 ? "y" : "ies"} (status=validated)`);
|
|
@@ -10636,15 +10633,15 @@ function registerMemoryApprove(memory2) {
|
|
|
10636
10633
|
frontmatter: { ...found.memory.frontmatter, status: "validated" },
|
|
10637
10634
|
body: found.memory.body
|
|
10638
10635
|
};
|
|
10639
|
-
await
|
|
10636
|
+
await writeFile18(found.filePath, serializeMemory16(next), "utf8");
|
|
10640
10637
|
ui.success(`Approved ${id} (status=validated)`);
|
|
10641
10638
|
ui.info(path20.relative(root, found.filePath));
|
|
10642
10639
|
});
|
|
10643
10640
|
}
|
|
10644
10641
|
|
|
10645
10642
|
// src/commands/memory-update.ts
|
|
10646
|
-
import { readFile as readFile12, writeFile as
|
|
10647
|
-
import { existsSync as
|
|
10643
|
+
import { readFile as readFile12, writeFile as writeFile19 } from "fs/promises";
|
|
10644
|
+
import { existsSync as existsSync38 } from "fs";
|
|
10648
10645
|
import path21 from "path";
|
|
10649
10646
|
import "commander";
|
|
10650
10647
|
import {
|
|
@@ -10656,12 +10653,12 @@ function registerMemoryUpdate(memory2) {
|
|
|
10656
10653
|
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--body-file <path>", "read new body from a Markdown file \u2014 for long content").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10657
10654
|
const root = findProjectRoot17(opts.dir);
|
|
10658
10655
|
const paths = resolveHaivePaths14(root);
|
|
10659
|
-
if (!
|
|
10656
|
+
if (!existsSync38(paths.memoriesDir)) {
|
|
10660
10657
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
10661
10658
|
process.exitCode = 1;
|
|
10662
10659
|
return;
|
|
10663
10660
|
}
|
|
10664
|
-
const memories = await
|
|
10661
|
+
const memories = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10665
10662
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
10666
10663
|
if (!loaded) {
|
|
10667
10664
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -10698,7 +10695,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
10698
10695
|
if (opts.author !== void 0) updated.push("author");
|
|
10699
10696
|
let newBody;
|
|
10700
10697
|
if (opts.bodyFile !== void 0) {
|
|
10701
|
-
if (!
|
|
10698
|
+
if (!existsSync38(opts.bodyFile)) {
|
|
10702
10699
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
10703
10700
|
process.exitCode = 1;
|
|
10704
10701
|
return;
|
|
@@ -10719,7 +10716,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
10719
10716
|
ui.warn("Nothing to update \u2014 provide at least one option.");
|
|
10720
10717
|
return;
|
|
10721
10718
|
}
|
|
10722
|
-
await
|
|
10719
|
+
await writeFile19(
|
|
10723
10720
|
loaded.filePath,
|
|
10724
10721
|
serializeMemory17({ frontmatter: newFrontmatter, body: newBody }),
|
|
10725
10722
|
"utf8"
|
|
@@ -10743,8 +10740,8 @@ function parseCsv3(value) {
|
|
|
10743
10740
|
}
|
|
10744
10741
|
|
|
10745
10742
|
// src/commands/memory-auto-promote.ts
|
|
10746
|
-
import { writeFile as
|
|
10747
|
-
import { existsSync as
|
|
10743
|
+
import { writeFile as writeFile20 } from "fs/promises";
|
|
10744
|
+
import { existsSync as existsSync39 } from "fs";
|
|
10748
10745
|
import path23 from "path";
|
|
10749
10746
|
import "commander";
|
|
10750
10747
|
import {
|
|
@@ -10764,7 +10761,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
10764
10761
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10765
10762
|
const root = findProjectRoot18(opts.dir);
|
|
10766
10763
|
const paths = resolveHaivePaths15(root);
|
|
10767
|
-
if (!
|
|
10764
|
+
if (!existsSync39(paths.memoriesDir)) {
|
|
10768
10765
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10769
10766
|
process.exitCode = 1;
|
|
10770
10767
|
return;
|
|
@@ -10773,7 +10770,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
10773
10770
|
minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE3.minReads),
|
|
10774
10771
|
maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
|
|
10775
10772
|
};
|
|
10776
|
-
const memories = await
|
|
10773
|
+
const memories = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10777
10774
|
const usage = await loadUsageIndex14(paths);
|
|
10778
10775
|
const eligible = memories.filter(
|
|
10779
10776
|
({ memory: memory3 }) => isAutoPromoteEligible3(memory3.frontmatter, getUsage12(usage, memory3.frontmatter.id), rule)
|
|
@@ -10796,7 +10793,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
10796
10793
|
frontmatter: { ...mem.frontmatter, status: "validated" },
|
|
10797
10794
|
body: mem.body
|
|
10798
10795
|
};
|
|
10799
|
-
await
|
|
10796
|
+
await writeFile20(filePath, serializeMemory18(next), "utf8");
|
|
10800
10797
|
written++;
|
|
10801
10798
|
}
|
|
10802
10799
|
}
|
|
@@ -10807,7 +10804,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
10807
10804
|
|
|
10808
10805
|
// src/commands/memory-edit.ts
|
|
10809
10806
|
import { spawn as spawn3 } from "child_process";
|
|
10810
|
-
import { existsSync as
|
|
10807
|
+
import { existsSync as existsSync40 } from "fs";
|
|
10811
10808
|
import { readFile as readFile13 } from "fs/promises";
|
|
10812
10809
|
import path24 from "path";
|
|
10813
10810
|
import "commander";
|
|
@@ -10820,12 +10817,12 @@ function registerMemoryEdit(memory2) {
|
|
|
10820
10817
|
memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
10821
10818
|
const root = findProjectRoot19(opts.dir);
|
|
10822
10819
|
const paths = resolveHaivePaths16(root);
|
|
10823
|
-
if (!
|
|
10820
|
+
if (!existsSync40(paths.memoriesDir)) {
|
|
10824
10821
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10825
10822
|
process.exitCode = 1;
|
|
10826
10823
|
return;
|
|
10827
10824
|
}
|
|
10828
|
-
const all = await
|
|
10825
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10829
10826
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
10830
10827
|
if (!found) {
|
|
10831
10828
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -10860,7 +10857,7 @@ function runEditor(editor, file) {
|
|
|
10860
10857
|
}
|
|
10861
10858
|
|
|
10862
10859
|
// src/commands/memory-for-files.ts
|
|
10863
|
-
import { existsSync as
|
|
10860
|
+
import { existsSync as existsSync41 } from "fs";
|
|
10864
10861
|
import path25 from "path";
|
|
10865
10862
|
import "commander";
|
|
10866
10863
|
import {
|
|
@@ -10876,12 +10873,12 @@ function registerMemoryForFiles(memory2) {
|
|
|
10876
10873
|
memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
|
|
10877
10874
|
const root = findProjectRoot20(opts.dir);
|
|
10878
10875
|
const paths = resolveHaivePaths17(root);
|
|
10879
|
-
if (!
|
|
10876
|
+
if (!existsSync41(paths.memoriesDir)) {
|
|
10880
10877
|
ui.error(`No .ai/memories at ${root}.`);
|
|
10881
10878
|
process.exitCode = 1;
|
|
10882
10879
|
return;
|
|
10883
10880
|
}
|
|
10884
|
-
const all = await
|
|
10881
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10885
10882
|
const usage = await loadUsageIndex15(paths);
|
|
10886
10883
|
const inferred = inferModulesFromPaths4(files);
|
|
10887
10884
|
const byAnchor = [];
|
|
@@ -10988,7 +10985,7 @@ function printGroup(root, label, loaded, usage) {
|
|
|
10988
10985
|
}
|
|
10989
10986
|
|
|
10990
10987
|
// src/commands/memory-hot.ts
|
|
10991
|
-
import { existsSync as
|
|
10988
|
+
import { existsSync as existsSync43 } from "fs";
|
|
10992
10989
|
import path26 from "path";
|
|
10993
10990
|
import "commander";
|
|
10994
10991
|
import {
|
|
@@ -11003,13 +11000,13 @@ function registerMemoryHot(memory2) {
|
|
|
11003
11000
|
).option("--threshold <n>", "minimum read_count to qualify (default: 3)", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11004
11001
|
const root = findProjectRoot21(opts.dir);
|
|
11005
11002
|
const paths = resolveHaivePaths18(root);
|
|
11006
|
-
if (!
|
|
11003
|
+
if (!existsSync43(paths.memoriesDir)) {
|
|
11007
11004
|
ui.error(`No .ai/memories at ${root}.`);
|
|
11008
11005
|
process.exitCode = 1;
|
|
11009
11006
|
return;
|
|
11010
11007
|
}
|
|
11011
11008
|
const threshold = Math.max(1, Number(opts.threshold ?? 3));
|
|
11012
|
-
const all = await
|
|
11009
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11013
11010
|
const usage = await loadUsageIndex16(paths);
|
|
11014
11011
|
const candidates = all.filter(({ memory: mem }) => {
|
|
11015
11012
|
const fm = mem.frontmatter;
|
|
@@ -11041,8 +11038,8 @@ function registerMemoryHot(memory2) {
|
|
|
11041
11038
|
}
|
|
11042
11039
|
|
|
11043
11040
|
// src/commands/memory-tried.ts
|
|
11044
|
-
import { mkdir as mkdir14, writeFile as
|
|
11045
|
-
import { existsSync as
|
|
11041
|
+
import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
|
|
11042
|
+
import { existsSync as existsSync44 } from "fs";
|
|
11046
11043
|
import path27 from "path";
|
|
11047
11044
|
import "commander";
|
|
11048
11045
|
import {
|
|
@@ -11072,7 +11069,7 @@ function registerMemoryTried(memory2) {
|
|
|
11072
11069
|
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11073
11070
|
const root = findProjectRoot22(opts.dir);
|
|
11074
11071
|
const paths = resolveHaivePaths19(root);
|
|
11075
|
-
if (!
|
|
11072
|
+
if (!existsSync44(paths.haiveDir)) {
|
|
11076
11073
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
11077
11074
|
process.exitCode = 1;
|
|
11078
11075
|
return;
|
|
@@ -11100,12 +11097,12 @@ function registerMemoryTried(memory2) {
|
|
|
11100
11097
|
}
|
|
11101
11098
|
const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
11102
11099
|
await mkdir14(path27.dirname(file), { recursive: true });
|
|
11103
|
-
if (
|
|
11100
|
+
if (existsSync44(file)) {
|
|
11104
11101
|
ui.error(`Memory already exists at ${file}`);
|
|
11105
11102
|
process.exitCode = 1;
|
|
11106
11103
|
return;
|
|
11107
11104
|
}
|
|
11108
|
-
await
|
|
11105
|
+
await writeFile21(file, serializeMemory19({ frontmatter, body }), "utf8");
|
|
11109
11106
|
ui.success(`Recorded: ${path27.relative(root, file)}`);
|
|
11110
11107
|
ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
|
|
11111
11108
|
if (sensor) ui.info(`sensor=regex warn autogen pattern=${JSON.stringify(sensor.pattern)}`);
|
|
@@ -11118,7 +11115,7 @@ function parseCsv4(value) {
|
|
|
11118
11115
|
|
|
11119
11116
|
// src/commands/memory-seed.ts
|
|
11120
11117
|
import { readFile as readFile14 } from "fs/promises";
|
|
11121
|
-
import { existsSync as
|
|
11118
|
+
import { existsSync as existsSync45 } from "fs";
|
|
11122
11119
|
import path28 from "path";
|
|
11123
11120
|
import "commander";
|
|
11124
11121
|
import {
|
|
@@ -11158,7 +11155,7 @@ function registerMemorySeed(memory2) {
|
|
|
11158
11155
|
}
|
|
11159
11156
|
return;
|
|
11160
11157
|
}
|
|
11161
|
-
if (!
|
|
11158
|
+
if (!existsSync45(paths.haiveDir)) {
|
|
11162
11159
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
11163
11160
|
process.exitCode = 1;
|
|
11164
11161
|
return;
|
|
@@ -11214,7 +11211,7 @@ function registerMemorySeed(memory2) {
|
|
|
11214
11211
|
}
|
|
11215
11212
|
|
|
11216
11213
|
// src/commands/memory-pending.ts
|
|
11217
|
-
import { existsSync as
|
|
11214
|
+
import { existsSync as existsSync46 } from "fs";
|
|
11218
11215
|
import path29 from "path";
|
|
11219
11216
|
import "commander";
|
|
11220
11217
|
import {
|
|
@@ -11227,12 +11224,12 @@ function registerMemoryPending(memory2) {
|
|
|
11227
11224
|
memory2.command("pending").description("List draft and proposed memories awaiting review (sorted by reads desc).\n\n draft = created but not yet activated \xB7 proposed = promoted, awaiting team validation").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11228
11225
|
const root = findProjectRoot24(opts.dir);
|
|
11229
11226
|
const paths = resolveHaivePaths21(root);
|
|
11230
|
-
if (!
|
|
11227
|
+
if (!existsSync46(paths.memoriesDir)) {
|
|
11231
11228
|
ui.error(`No .ai/memories at ${root}.`);
|
|
11232
11229
|
process.exitCode = 1;
|
|
11233
11230
|
return;
|
|
11234
11231
|
}
|
|
11235
|
-
const all = await
|
|
11232
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11236
11233
|
const usage = await loadUsageIndex17(paths);
|
|
11237
11234
|
const filterFn = ({ memory: mem }) => {
|
|
11238
11235
|
if (mem.frontmatter.status !== "proposed" && mem.frontmatter.status !== "draft") return false;
|
|
@@ -11285,7 +11282,7 @@ function registerMemoryPending(memory2) {
|
|
|
11285
11282
|
}
|
|
11286
11283
|
|
|
11287
11284
|
// src/commands/memory-query.ts
|
|
11288
|
-
import { existsSync as
|
|
11285
|
+
import { existsSync as existsSync47 } from "fs";
|
|
11289
11286
|
import path30 from "path";
|
|
11290
11287
|
import "commander";
|
|
11291
11288
|
import {
|
|
@@ -11302,7 +11299,7 @@ function registerMemoryQuery(memory2) {
|
|
|
11302
11299
|
memory2.command("search <text>").alias("query").description("Search memories by id, tag, or substring (AND, OR fallback). Mirrors MCP mem_search. Alias: query").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
|
|
11303
11300
|
const root = findProjectRoot25(opts.dir);
|
|
11304
11301
|
const paths = resolveHaivePaths22(root);
|
|
11305
|
-
if (!
|
|
11302
|
+
if (!existsSync47(paths.memoriesDir)) {
|
|
11306
11303
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
11307
11304
|
process.exitCode = 1;
|
|
11308
11305
|
return;
|
|
@@ -11313,7 +11310,7 @@ function registerMemoryQuery(memory2) {
|
|
|
11313
11310
|
return;
|
|
11314
11311
|
}
|
|
11315
11312
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
11316
|
-
const all = await
|
|
11313
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11317
11314
|
const passesFilters2 = (mem) => {
|
|
11318
11315
|
const fm = mem.frontmatter;
|
|
11319
11316
|
if (opts.scope && fm.scope !== opts.scope) return false;
|
|
@@ -11360,8 +11357,8 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
11360
11357
|
}
|
|
11361
11358
|
|
|
11362
11359
|
// src/commands/memory-reject.ts
|
|
11363
|
-
import { writeFile as
|
|
11364
|
-
import { existsSync as
|
|
11360
|
+
import { writeFile as writeFile23 } from "fs/promises";
|
|
11361
|
+
import { existsSync as existsSync48 } from "fs";
|
|
11365
11362
|
import "commander";
|
|
11366
11363
|
import {
|
|
11367
11364
|
findProjectRoot as findProjectRoot26,
|
|
@@ -11375,19 +11372,19 @@ function registerMemoryReject(memory2) {
|
|
|
11375
11372
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
11376
11373
|
const root = findProjectRoot26(opts.dir);
|
|
11377
11374
|
const paths = resolveHaivePaths23(root);
|
|
11378
|
-
if (!
|
|
11375
|
+
if (!existsSync48(paths.memoriesDir)) {
|
|
11379
11376
|
ui.error(`No .ai/memories at ${root}.`);
|
|
11380
11377
|
process.exitCode = 1;
|
|
11381
11378
|
return;
|
|
11382
11379
|
}
|
|
11383
|
-
const memories = await
|
|
11380
|
+
const memories = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11384
11381
|
const loaded = memories.find((m) => m.memory.frontmatter.id === id);
|
|
11385
11382
|
if (!loaded) {
|
|
11386
11383
|
ui.error(`No memory with id "${id}".`);
|
|
11387
11384
|
process.exitCode = 1;
|
|
11388
11385
|
return;
|
|
11389
11386
|
}
|
|
11390
|
-
await
|
|
11387
|
+
await writeFile23(
|
|
11391
11388
|
loaded.filePath,
|
|
11392
11389
|
serializeMemory20({
|
|
11393
11390
|
frontmatter: {
|
|
@@ -11411,7 +11408,7 @@ function registerMemoryReject(memory2) {
|
|
|
11411
11408
|
}
|
|
11412
11409
|
|
|
11413
11410
|
// src/commands/memory-rm.ts
|
|
11414
|
-
import { existsSync as
|
|
11411
|
+
import { existsSync as existsSync49 } from "fs";
|
|
11415
11412
|
import { unlink as unlink3 } from "fs/promises";
|
|
11416
11413
|
import path31 from "path";
|
|
11417
11414
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
@@ -11426,12 +11423,12 @@ function registerMemoryRm(memory2) {
|
|
|
11426
11423
|
memory2.command("delete <id>").alias("rm").description("Delete a memory file (and its usage entry by default). Mirrors MCP mem_delete. Alias: rm").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
11427
11424
|
const root = findProjectRoot27(opts.dir);
|
|
11428
11425
|
const paths = resolveHaivePaths24(root);
|
|
11429
|
-
if (!
|
|
11426
|
+
if (!existsSync49(paths.memoriesDir)) {
|
|
11430
11427
|
ui.error(`No .ai/memories at ${root}.`);
|
|
11431
11428
|
process.exitCode = 1;
|
|
11432
11429
|
return;
|
|
11433
11430
|
}
|
|
11434
|
-
const all = await
|
|
11431
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11435
11432
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
11436
11433
|
if (!found) {
|
|
11437
11434
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -11462,7 +11459,7 @@ function registerMemoryRm(memory2) {
|
|
|
11462
11459
|
}
|
|
11463
11460
|
|
|
11464
11461
|
// src/commands/memory-show.ts
|
|
11465
|
-
import { existsSync as
|
|
11462
|
+
import { existsSync as existsSync50 } from "fs";
|
|
11466
11463
|
import { readFile as readFile15 } from "fs/promises";
|
|
11467
11464
|
import path33 from "path";
|
|
11468
11465
|
import "commander";
|
|
@@ -11477,12 +11474,12 @@ function registerMemoryShow(memory2) {
|
|
|
11477
11474
|
memory2.command("get <id>").alias("show").description("Print a memory's frontmatter, body, and confidence/usage. Mirrors MCP mem_get. Alias: show").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
11478
11475
|
const root = findProjectRoot28(opts.dir);
|
|
11479
11476
|
const paths = resolveHaivePaths25(root);
|
|
11480
|
-
if (!
|
|
11477
|
+
if (!existsSync50(paths.memoriesDir)) {
|
|
11481
11478
|
ui.error(`No .ai/memories at ${root}.`);
|
|
11482
11479
|
process.exitCode = 1;
|
|
11483
11480
|
return;
|
|
11484
11481
|
}
|
|
11485
|
-
const all = await
|
|
11482
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11486
11483
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
11487
11484
|
if (!found) {
|
|
11488
11485
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -11521,7 +11518,7 @@ function registerMemoryShow(memory2) {
|
|
|
11521
11518
|
}
|
|
11522
11519
|
|
|
11523
11520
|
// src/commands/memory-stats.ts
|
|
11524
|
-
import { existsSync as
|
|
11521
|
+
import { existsSync as existsSync51 } from "fs";
|
|
11525
11522
|
import path34 from "path";
|
|
11526
11523
|
import "commander";
|
|
11527
11524
|
import {
|
|
@@ -11535,12 +11532,12 @@ function registerMemoryStats(memory2) {
|
|
|
11535
11532
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11536
11533
|
const root = findProjectRoot29(opts.dir);
|
|
11537
11534
|
const paths = resolveHaivePaths26(root);
|
|
11538
|
-
if (!
|
|
11535
|
+
if (!existsSync51(paths.memoriesDir)) {
|
|
11539
11536
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
11540
11537
|
process.exitCode = 1;
|
|
11541
11538
|
return;
|
|
11542
11539
|
}
|
|
11543
|
-
const all = await
|
|
11540
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11544
11541
|
const usage = await loadUsageIndex21(paths);
|
|
11545
11542
|
const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
11546
11543
|
if (target.length === 0) {
|
|
@@ -11566,7 +11563,7 @@ function registerMemoryStats(memory2) {
|
|
|
11566
11563
|
}
|
|
11567
11564
|
|
|
11568
11565
|
// src/commands/memory-impact.ts
|
|
11569
|
-
import { existsSync as
|
|
11566
|
+
import { existsSync as existsSync53 } from "fs";
|
|
11570
11567
|
import "commander";
|
|
11571
11568
|
import {
|
|
11572
11569
|
compareImpact,
|
|
@@ -11583,12 +11580,12 @@ function registerMemoryImpact(memory2) {
|
|
|
11583
11580
|
).option("--id <id>", "show impact for a single memory id").option("--prune", "list only prune candidates (dead weight worth reviewing)", false).option("--tier <tier>", "filter to a tier: high | medium | low | dormant").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11584
11581
|
const root = findProjectRoot30(opts.dir);
|
|
11585
11582
|
const paths = resolveHaivePaths27(root);
|
|
11586
|
-
if (!
|
|
11583
|
+
if (!existsSync53(paths.memoriesDir)) {
|
|
11587
11584
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
11588
11585
|
process.exitCode = 1;
|
|
11589
11586
|
return;
|
|
11590
11587
|
}
|
|
11591
|
-
const all = await
|
|
11588
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11592
11589
|
const usageIndex = await loadUsageIndex23(paths);
|
|
11593
11590
|
let rows = all.filter((m) => !opts.id || m.memory.frontmatter.id === opts.id).map(({ memory: mem }) => {
|
|
11594
11591
|
const fm = mem.frontmatter;
|
|
@@ -11654,8 +11651,8 @@ function pad(value, width) {
|
|
|
11654
11651
|
}
|
|
11655
11652
|
|
|
11656
11653
|
// src/commands/memory-feedback.ts
|
|
11657
|
-
import { existsSync as
|
|
11658
|
-
import { writeFile as
|
|
11654
|
+
import { existsSync as existsSync54 } from "fs";
|
|
11655
|
+
import { writeFile as writeFile24 } from "fs/promises";
|
|
11659
11656
|
import "commander";
|
|
11660
11657
|
import {
|
|
11661
11658
|
applyFeedbackAdjustment as applyFeedbackAdjustment2,
|
|
@@ -11681,12 +11678,12 @@ function registerMemoryFeedback(memory2) {
|
|
|
11681
11678
|
}
|
|
11682
11679
|
const root = findProjectRoot31(opts.dir);
|
|
11683
11680
|
const paths = resolveHaivePaths28(root);
|
|
11684
|
-
if (!
|
|
11681
|
+
if (!existsSync54(paths.memoriesDir)) {
|
|
11685
11682
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
11686
11683
|
process.exitCode = 1;
|
|
11687
11684
|
return;
|
|
11688
11685
|
}
|
|
11689
|
-
const all = await
|
|
11686
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11690
11687
|
const target = all.find((m) => m.memory.frontmatter.id === id);
|
|
11691
11688
|
if (!target) {
|
|
11692
11689
|
ui.error(`No memory with id '${id}'.`);
|
|
@@ -11703,7 +11700,7 @@ function registerMemoryFeedback(memory2) {
|
|
|
11703
11700
|
const adjustedFrontmatter = applyFeedbackAdjustment2(target.memory.frontmatter, adjustment);
|
|
11704
11701
|
if (adjustedFrontmatter !== target.memory.frontmatter) {
|
|
11705
11702
|
target.memory.frontmatter = adjustedFrontmatter;
|
|
11706
|
-
await
|
|
11703
|
+
await writeFile24(target.filePath, serializeMemory21(target.memory), "utf8");
|
|
11707
11704
|
}
|
|
11708
11705
|
const impact = computeImpact4(target.memory.frontmatter, usage);
|
|
11709
11706
|
if (opts.json) {
|
|
@@ -11721,8 +11718,8 @@ function registerMemoryFeedback(memory2) {
|
|
|
11721
11718
|
}
|
|
11722
11719
|
|
|
11723
11720
|
// src/commands/memory-verify.ts
|
|
11724
|
-
import { writeFile as
|
|
11725
|
-
import { existsSync as
|
|
11721
|
+
import { writeFile as writeFile25 } from "fs/promises";
|
|
11722
|
+
import { existsSync as existsSync55 } from "fs";
|
|
11726
11723
|
import path35 from "path";
|
|
11727
11724
|
import "commander";
|
|
11728
11725
|
import {
|
|
@@ -11737,7 +11734,7 @@ function registerMemoryVerify(memory2) {
|
|
|
11737
11734
|
).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11738
11735
|
const root = findProjectRoot32(opts.dir);
|
|
11739
11736
|
const paths = resolveHaivePaths29(root);
|
|
11740
|
-
if (!
|
|
11737
|
+
if (!existsSync55(paths.memoriesDir)) {
|
|
11741
11738
|
if (opts.json) {
|
|
11742
11739
|
console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
|
|
11743
11740
|
} else {
|
|
@@ -11746,7 +11743,7 @@ function registerMemoryVerify(memory2) {
|
|
|
11746
11743
|
process.exitCode = 1;
|
|
11747
11744
|
return;
|
|
11748
11745
|
}
|
|
11749
|
-
const all = await
|
|
11746
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
11750
11747
|
const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
|
|
11751
11748
|
if (opts.id && targets.length === 0) {
|
|
11752
11749
|
if (opts.json) {
|
|
@@ -11795,7 +11792,7 @@ function registerMemoryVerify(memory2) {
|
|
|
11795
11792
|
}
|
|
11796
11793
|
if (opts.update) {
|
|
11797
11794
|
const next = applyVerification2(mem, result);
|
|
11798
|
-
await
|
|
11795
|
+
await writeFile25(filePath, serializeMemory23(next), "utf8");
|
|
11799
11796
|
updated++;
|
|
11800
11797
|
}
|
|
11801
11798
|
}
|
|
@@ -11858,7 +11855,7 @@ function applyVerification2(mem, result) {
|
|
|
11858
11855
|
|
|
11859
11856
|
// src/commands/memory-import.ts
|
|
11860
11857
|
import { readFile as readFile16 } from "fs/promises";
|
|
11861
|
-
import { existsSync as
|
|
11858
|
+
import { existsSync as existsSync56 } from "fs";
|
|
11862
11859
|
import "commander";
|
|
11863
11860
|
import {
|
|
11864
11861
|
findProjectRoot as findProjectRoot33,
|
|
@@ -11870,12 +11867,12 @@ function registerMemoryImport(memory2) {
|
|
|
11870
11867
|
).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11871
11868
|
const root = findProjectRoot33(opts.dir);
|
|
11872
11869
|
const paths = resolveHaivePaths30(root);
|
|
11873
|
-
if (!
|
|
11870
|
+
if (!existsSync56(paths.haiveDir)) {
|
|
11874
11871
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
11875
11872
|
process.exitCode = 1;
|
|
11876
11873
|
return;
|
|
11877
11874
|
}
|
|
11878
|
-
if (!
|
|
11875
|
+
if (!existsSync56(opts.from)) {
|
|
11879
11876
|
ui.error(`File not found: ${opts.from}`);
|
|
11880
11877
|
process.exitCode = 1;
|
|
11881
11878
|
return;
|
|
@@ -11908,8 +11905,8 @@ function registerMemoryImport(memory2) {
|
|
|
11908
11905
|
}
|
|
11909
11906
|
|
|
11910
11907
|
// src/commands/memory-import-changelog.ts
|
|
11911
|
-
import { existsSync as
|
|
11912
|
-
import { readFile as readFile17, mkdir as mkdir15, writeFile as
|
|
11908
|
+
import { existsSync as existsSync57 } from "fs";
|
|
11909
|
+
import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile26 } from "fs/promises";
|
|
11913
11910
|
import path36 from "path";
|
|
11914
11911
|
import "commander";
|
|
11915
11912
|
import {
|
|
@@ -11983,7 +11980,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
11983
11980
|
const root = findProjectRoot34(opts.dir);
|
|
11984
11981
|
const paths = resolveHaivePaths31(root);
|
|
11985
11982
|
const changelogPath = path36.resolve(root, opts.fromChangelog);
|
|
11986
|
-
if (!
|
|
11983
|
+
if (!existsSync57(changelogPath)) {
|
|
11987
11984
|
ui.error(`CHANGELOG not found: ${changelogPath}`);
|
|
11988
11985
|
process.exitCode = 1;
|
|
11989
11986
|
return;
|
|
@@ -12046,7 +12043,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
12046
12043
|
paths: [path36.relative(root, changelogPath)],
|
|
12047
12044
|
topic: `changelog-${pkgName}-${entry.version}`
|
|
12048
12045
|
});
|
|
12049
|
-
await
|
|
12046
|
+
await writeFile26(
|
|
12050
12047
|
path36.join(teamDir, `${fm.id}.md`),
|
|
12051
12048
|
serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
|
|
12052
12049
|
"utf8"
|
|
@@ -12070,15 +12067,15 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
12070
12067
|
}
|
|
12071
12068
|
|
|
12072
12069
|
// src/commands/memory-digest.ts
|
|
12073
|
-
import { existsSync as
|
|
12074
|
-
import { writeFile as
|
|
12070
|
+
import { existsSync as existsSync58 } from "fs";
|
|
12071
|
+
import { writeFile as writeFile27 } from "fs/promises";
|
|
12075
12072
|
import path37 from "path";
|
|
12076
12073
|
import "commander";
|
|
12077
12074
|
import {
|
|
12078
12075
|
deriveConfidence as deriveConfidence12,
|
|
12079
12076
|
findProjectRoot as findProjectRoot35,
|
|
12080
12077
|
getUsage as getUsage20,
|
|
12081
|
-
loadMemoriesFromDir as
|
|
12078
|
+
loadMemoriesFromDir as loadMemoriesFromDir28,
|
|
12082
12079
|
loadUsageIndex as loadUsageIndex25,
|
|
12083
12080
|
resolveHaivePaths as resolveHaivePaths32
|
|
12084
12081
|
} from "@hiveai/core";
|
|
@@ -12095,7 +12092,7 @@ function registerMemoryDigest(program2) {
|
|
|
12095
12092
|
).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12096
12093
|
const root = findProjectRoot35(opts.dir);
|
|
12097
12094
|
const paths = resolveHaivePaths32(root);
|
|
12098
|
-
if (!
|
|
12095
|
+
if (!existsSync58(paths.memoriesDir)) {
|
|
12099
12096
|
ui.error("No .ai/memories found. Run `haive init` first.");
|
|
12100
12097
|
process.exitCode = 1;
|
|
12101
12098
|
return;
|
|
@@ -12103,7 +12100,7 @@ function registerMemoryDigest(program2) {
|
|
|
12103
12100
|
const days = Math.max(1, Number(opts.days ?? 7));
|
|
12104
12101
|
const scopeFilter = opts.scope ?? "team";
|
|
12105
12102
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
12106
|
-
const all = await
|
|
12103
|
+
const all = await loadMemoriesFromDir28(paths.memoriesDir);
|
|
12107
12104
|
const usage = await loadUsageIndex25(paths);
|
|
12108
12105
|
const recent = all.filter(({ memory: mem }) => {
|
|
12109
12106
|
const fm = mem.frontmatter;
|
|
@@ -12168,7 +12165,7 @@ function registerMemoryDigest(program2) {
|
|
|
12168
12165
|
const digest = lines.join("\n");
|
|
12169
12166
|
if (opts.out) {
|
|
12170
12167
|
const outPath = path37.resolve(process.cwd(), opts.out);
|
|
12171
|
-
await
|
|
12168
|
+
await writeFile27(outPath, digest, "utf8");
|
|
12172
12169
|
ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
|
|
12173
12170
|
} else {
|
|
12174
12171
|
console.log(digest);
|
|
@@ -12177,15 +12174,15 @@ function registerMemoryDigest(program2) {
|
|
|
12177
12174
|
}
|
|
12178
12175
|
|
|
12179
12176
|
// src/commands/session-end.ts
|
|
12180
|
-
import { writeFile as
|
|
12181
|
-
import { existsSync as
|
|
12177
|
+
import { writeFile as writeFile28, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
|
|
12178
|
+
import { existsSync as existsSync59 } from "fs";
|
|
12182
12179
|
import { spawn as spawn4 } from "child_process";
|
|
12183
12180
|
import path38 from "path";
|
|
12184
12181
|
import "commander";
|
|
12185
12182
|
import {
|
|
12186
12183
|
buildFrontmatter as buildFrontmatter10,
|
|
12187
12184
|
findProjectRoot as findProjectRoot36,
|
|
12188
|
-
loadMemoriesFromDir as
|
|
12185
|
+
loadMemoriesFromDir as loadMemoriesFromDir29,
|
|
12189
12186
|
loadPreventionEvents as loadPreventionEvents2,
|
|
12190
12187
|
loadUsageIndex as loadUsageIndex26,
|
|
12191
12188
|
memoryFilePath as memoryFilePath10,
|
|
@@ -12196,7 +12193,7 @@ import {
|
|
|
12196
12193
|
} from "@hiveai/core";
|
|
12197
12194
|
async function buildAutoRecap(paths) {
|
|
12198
12195
|
const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
12199
|
-
if (!
|
|
12196
|
+
if (!existsSync59(obsFile)) return await buildGitAutoRecap(paths);
|
|
12200
12197
|
const raw = await readFile18(obsFile, "utf8").catch(() => "");
|
|
12201
12198
|
if (!raw.trim()) return await buildGitAutoRecap(paths);
|
|
12202
12199
|
const lines = raw.split("\n").filter(Boolean);
|
|
@@ -12365,7 +12362,7 @@ function runGit(cwd, args) {
|
|
|
12365
12362
|
}
|
|
12366
12363
|
async function observationStart(paths) {
|
|
12367
12364
|
const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
12368
|
-
if (!
|
|
12365
|
+
if (!existsSync59(obsFile)) return null;
|
|
12369
12366
|
const raw = await readFile18(obsFile, "utf8").catch(() => "");
|
|
12370
12367
|
let first = null;
|
|
12371
12368
|
for (const line of raw.split("\n")) {
|
|
@@ -12381,7 +12378,7 @@ async function observationStart(paths) {
|
|
|
12381
12378
|
}
|
|
12382
12379
|
async function printCaughtForYou(paths, since, quiet) {
|
|
12383
12380
|
if (quiet) return;
|
|
12384
|
-
const memories =
|
|
12381
|
+
const memories = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir29(paths.memoriesDir) : [];
|
|
12385
12382
|
const usage = await loadUsageIndex26(paths);
|
|
12386
12383
|
const events = await loadPreventionEvents2(paths);
|
|
12387
12384
|
const summary = summarizeCaughtForYou(events, memories, usage, {
|
|
@@ -12419,7 +12416,7 @@ function registerSessionEnd(session2) {
|
|
|
12419
12416
|
).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12420
12417
|
const root = findProjectRoot36(opts.dir);
|
|
12421
12418
|
const paths = resolveHaivePaths33(root);
|
|
12422
|
-
if (!
|
|
12419
|
+
if (!existsSync59(paths.haiveDir)) {
|
|
12423
12420
|
if (opts.auto || opts.quiet) return;
|
|
12424
12421
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
12425
12422
|
process.exitCode = 1;
|
|
@@ -12453,7 +12450,7 @@ function registerSessionEnd(session2) {
|
|
|
12453
12450
|
});
|
|
12454
12451
|
const topic = recapTopic2(scope, opts.module);
|
|
12455
12452
|
const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
|
|
12456
|
-
const missingPaths = filesTouched.filter((p) => !
|
|
12453
|
+
const missingPaths = filesTouched.filter((p) => !existsSync59(path38.resolve(root, p)));
|
|
12457
12454
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
12458
12455
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
12459
12456
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
@@ -12461,11 +12458,11 @@ function registerSessionEnd(session2) {
|
|
|
12461
12458
|
const cleanupObservations = async () => {
|
|
12462
12459
|
if (!opts.auto) return;
|
|
12463
12460
|
const obsFile = path38.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
12464
|
-
if (
|
|
12461
|
+
if (existsSync59(obsFile)) await rm2(obsFile).catch(() => {
|
|
12465
12462
|
});
|
|
12466
12463
|
};
|
|
12467
|
-
if (
|
|
12468
|
-
const existing = await
|
|
12464
|
+
if (existsSync59(paths.memoriesDir)) {
|
|
12465
|
+
const existing = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
12469
12466
|
const topicMatch = existing.find(
|
|
12470
12467
|
({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
|
|
12471
12468
|
);
|
|
@@ -12481,7 +12478,7 @@ function registerSessionEnd(session2) {
|
|
|
12481
12478
|
paths: filesTouched.length ? filesTouched : fm.anchor.paths
|
|
12482
12479
|
}
|
|
12483
12480
|
};
|
|
12484
|
-
await
|
|
12481
|
+
await writeFile28(topicMatch.filePath, serializeMemory25({ frontmatter: newFrontmatter, body }), "utf8");
|
|
12485
12482
|
await cleanupObservations();
|
|
12486
12483
|
if (!opts.quiet) {
|
|
12487
12484
|
ui.success(`Session recap updated (revision #${revisionCount})`);
|
|
@@ -12504,7 +12501,7 @@ function registerSessionEnd(session2) {
|
|
|
12504
12501
|
});
|
|
12505
12502
|
const file = memoryFilePath10(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
12506
12503
|
await mkdir16(path38.dirname(file), { recursive: true });
|
|
12507
|
-
await
|
|
12504
|
+
await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
|
|
12508
12505
|
await cleanupObservations();
|
|
12509
12506
|
if (!opts.quiet) {
|
|
12510
12507
|
ui.success(`Session recap created`);
|
|
@@ -12528,7 +12525,7 @@ function normalizeAnchorPath(root, filePath) {
|
|
|
12528
12525
|
}
|
|
12529
12526
|
|
|
12530
12527
|
// src/commands/snapshot.ts
|
|
12531
|
-
import { existsSync as
|
|
12528
|
+
import { existsSync as existsSync60 } from "fs";
|
|
12532
12529
|
import { readdir as readdir4 } from "fs/promises";
|
|
12533
12530
|
import path39 from "path";
|
|
12534
12531
|
import "commander";
|
|
@@ -12563,14 +12560,14 @@ function registerSnapshot(program2) {
|
|
|
12563
12560
|
).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12564
12561
|
const root = findProjectRoot37(opts.dir);
|
|
12565
12562
|
const paths = resolveHaivePaths34(root);
|
|
12566
|
-
if (!
|
|
12563
|
+
if (!existsSync60(paths.haiveDir)) {
|
|
12567
12564
|
ui.error("No .ai/ found. Run `haive init` first.");
|
|
12568
12565
|
process.exitCode = 1;
|
|
12569
12566
|
return;
|
|
12570
12567
|
}
|
|
12571
12568
|
if (opts.list) {
|
|
12572
12569
|
const contractsDir = path39.join(paths.haiveDir, "contracts");
|
|
12573
|
-
if (!
|
|
12570
|
+
if (!existsSync60(contractsDir)) {
|
|
12574
12571
|
console.log(ui.dim("No contract snapshots found."));
|
|
12575
12572
|
return;
|
|
12576
12573
|
}
|
|
@@ -12694,15 +12691,15 @@ function detectFormat(filePath) {
|
|
|
12694
12691
|
}
|
|
12695
12692
|
|
|
12696
12693
|
// src/commands/hub.ts
|
|
12697
|
-
import { existsSync as
|
|
12698
|
-
import { mkdir as mkdir17, readFile as readFile19, writeFile as
|
|
12694
|
+
import { existsSync as existsSync61 } from "fs";
|
|
12695
|
+
import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
|
|
12699
12696
|
import path40 from "path";
|
|
12700
12697
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
12701
12698
|
import "commander";
|
|
12702
12699
|
import {
|
|
12703
12700
|
findProjectRoot as findProjectRoot38,
|
|
12704
12701
|
loadConfig as loadConfig8,
|
|
12705
|
-
loadMemoriesFromDir as
|
|
12702
|
+
loadMemoriesFromDir as loadMemoriesFromDir30,
|
|
12706
12703
|
resolveHaivePaths as resolveHaivePaths35,
|
|
12707
12704
|
saveConfig as saveConfig3,
|
|
12708
12705
|
serializeMemory as serializeMemory26
|
|
@@ -12728,7 +12725,7 @@ function registerHub(program2) {
|
|
|
12728
12725
|
}
|
|
12729
12726
|
const sharedDir = path40.join(absPath, ".ai", "memories", "shared");
|
|
12730
12727
|
await mkdir17(sharedDir, { recursive: true });
|
|
12731
|
-
await
|
|
12728
|
+
await writeFile29(
|
|
12732
12729
|
path40.join(absPath, ".ai", "README.md"),
|
|
12733
12730
|
`# hAIve Team Knowledge Hub
|
|
12734
12731
|
|
|
@@ -12750,7 +12747,7 @@ haive hub pull # import into a project
|
|
|
12750
12747
|
`,
|
|
12751
12748
|
"utf8"
|
|
12752
12749
|
);
|
|
12753
|
-
await
|
|
12750
|
+
await writeFile29(
|
|
12754
12751
|
path40.join(absPath, ".gitignore"),
|
|
12755
12752
|
".ai/.cache/\n.ai/memories/personal/\n",
|
|
12756
12753
|
"utf8"
|
|
@@ -12796,7 +12793,7 @@ Next steps:
|
|
|
12796
12793
|
return;
|
|
12797
12794
|
}
|
|
12798
12795
|
const hubRoot = path40.resolve(root, config.hubPath);
|
|
12799
|
-
if (!
|
|
12796
|
+
if (!existsSync61(hubRoot)) {
|
|
12800
12797
|
ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
|
|
12801
12798
|
process.exitCode = 1;
|
|
12802
12799
|
return;
|
|
@@ -12804,7 +12801,7 @@ Next steps:
|
|
|
12804
12801
|
const projectName = path40.basename(root);
|
|
12805
12802
|
const destDir = path40.join(hubRoot, ".ai", "memories", "shared", projectName);
|
|
12806
12803
|
await mkdir17(destDir, { recursive: true });
|
|
12807
|
-
const all = await
|
|
12804
|
+
const all = await loadMemoriesFromDir30(paths.memoriesDir);
|
|
12808
12805
|
const shared = all.filter(
|
|
12809
12806
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
|
|
12810
12807
|
!memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
|
|
@@ -12822,7 +12819,7 @@ Next steps:
|
|
|
12822
12819
|
const fm = memory2.frontmatter;
|
|
12823
12820
|
const fileName = `${fm.id}.md`;
|
|
12824
12821
|
const destPath = path40.join(destDir, fileName);
|
|
12825
|
-
await
|
|
12822
|
+
await writeFile29(destPath, serializeMemory26(memory2), "utf8");
|
|
12826
12823
|
pushed++;
|
|
12827
12824
|
}
|
|
12828
12825
|
console.log(ui.green(`\u2713 Pushed ${pushed} shared memor${pushed === 1 ? "y" : "ies"} to hub`));
|
|
@@ -12866,7 +12863,7 @@ Next steps:
|
|
|
12866
12863
|
}
|
|
12867
12864
|
const hubRoot = path40.resolve(root, config.hubPath);
|
|
12868
12865
|
const hubSharedDir = path40.join(hubRoot, ".ai", "memories", "shared");
|
|
12869
|
-
if (!
|
|
12866
|
+
if (!existsSync61(hubSharedDir)) {
|
|
12870
12867
|
ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
|
|
12871
12868
|
return;
|
|
12872
12869
|
}
|
|
@@ -12921,7 +12918,7 @@ Next steps:
|
|
|
12921
12918
|
` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
|
|
12922
12919
|
);
|
|
12923
12920
|
const sharedDir = path40.join(paths.memoriesDir, "shared");
|
|
12924
|
-
if (
|
|
12921
|
+
if (existsSync61(sharedDir)) {
|
|
12925
12922
|
const { readdir: readdir7 } = await import("fs/promises");
|
|
12926
12923
|
const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12927
12924
|
console.log(`
|
|
@@ -12933,7 +12930,7 @@ Next steps:
|
|
|
12933
12930
|
} else {
|
|
12934
12931
|
console.log(ui.dim(" No imported shared memories yet."));
|
|
12935
12932
|
}
|
|
12936
|
-
const all = await
|
|
12933
|
+
const all = await loadMemoriesFromDir30(paths.memoriesDir);
|
|
12937
12934
|
const outgoing = all.filter(
|
|
12938
12935
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
|
|
12939
12936
|
);
|
|
@@ -12943,20 +12940,20 @@ Next steps:
|
|
|
12943
12940
|
console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
|
|
12944
12941
|
}
|
|
12945
12942
|
void readFile19;
|
|
12946
|
-
void
|
|
12943
|
+
void writeFile29;
|
|
12947
12944
|
void saveConfig3;
|
|
12948
12945
|
});
|
|
12949
12946
|
}
|
|
12950
12947
|
|
|
12951
12948
|
// src/commands/stats.ts
|
|
12952
12949
|
import "commander";
|
|
12953
|
-
import { existsSync as
|
|
12954
|
-
import { mkdir as mkdir18, writeFile as
|
|
12950
|
+
import { existsSync as existsSync63 } from "fs";
|
|
12951
|
+
import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
|
|
12955
12952
|
import path41 from "path";
|
|
12956
12953
|
import {
|
|
12957
12954
|
aggregateUsage,
|
|
12958
12955
|
findProjectRoot as findProjectRoot39,
|
|
12959
|
-
loadMemoriesFromDir as
|
|
12956
|
+
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
12960
12957
|
loadUsageIndex as loadUsageIndex27,
|
|
12961
12958
|
parseSince,
|
|
12962
12959
|
readUsageEvents as readUsageEvents2,
|
|
@@ -13028,8 +13025,8 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
13028
13025
|
const size = await usageLogSize(paths);
|
|
13029
13026
|
let events = await readUsageEvents2(paths);
|
|
13030
13027
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
13031
|
-
if (
|
|
13032
|
-
const mems = await
|
|
13028
|
+
if (existsSync63(paths.memoriesDir)) {
|
|
13029
|
+
const mems = await loadMemoriesFromDir31(paths.memoriesDir);
|
|
13033
13030
|
for (const { memory: memory2 } of mems) {
|
|
13034
13031
|
const fm = memory2.frontmatter;
|
|
13035
13032
|
if (fm.type === "session_recap") memoryCount.total_skipped_session++;
|
|
@@ -13071,7 +13068,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
13071
13068
|
top_memory_reads: memoryHitsLeader,
|
|
13072
13069
|
roi_hints: roiHints
|
|
13073
13070
|
};
|
|
13074
|
-
await
|
|
13071
|
+
await writeFile30(outAbs, JSON.stringify(payload, null, 2), "utf8");
|
|
13075
13072
|
ui.success(`Wrote ROI / usage rollup \u2192 ${outAbs}`);
|
|
13076
13073
|
}
|
|
13077
13074
|
async function renderMemoryHits(paths, opts) {
|
|
@@ -13248,8 +13245,8 @@ function summarize(name, t0, payload, notes) {
|
|
|
13248
13245
|
}
|
|
13249
13246
|
|
|
13250
13247
|
// src/commands/benchmark.ts
|
|
13251
|
-
import { existsSync as
|
|
13252
|
-
import { readdir as readdir5, readFile as readFile20, writeFile as
|
|
13248
|
+
import { existsSync as existsSync64 } from "fs";
|
|
13249
|
+
import { readdir as readdir5, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
|
|
13253
13250
|
import path43 from "path";
|
|
13254
13251
|
import "commander";
|
|
13255
13252
|
import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hiveai/core";
|
|
@@ -13266,7 +13263,7 @@ function registerBenchmark(program2) {
|
|
|
13266
13263
|
const markdown = renderMarkdown(root, summary, rows);
|
|
13267
13264
|
if (opts.out) {
|
|
13268
13265
|
const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
|
|
13269
|
-
await
|
|
13266
|
+
await writeFile31(outFile, markdown, "utf8");
|
|
13270
13267
|
ui.success(`wrote ${path43.relative(process.cwd(), outFile)}`);
|
|
13271
13268
|
return;
|
|
13272
13269
|
}
|
|
@@ -13296,14 +13293,14 @@ function resolveBenchmarkRoot(dir) {
|
|
|
13296
13293
|
return path43.join(projectRoot, candidate);
|
|
13297
13294
|
}
|
|
13298
13295
|
async function collectRows(root) {
|
|
13299
|
-
if (!
|
|
13296
|
+
if (!existsSync64(root)) throw new Error(`Benchmark directory not found: ${root}`);
|
|
13300
13297
|
const entries = await readdir5(root, { withFileTypes: true });
|
|
13301
13298
|
const rows = [];
|
|
13302
13299
|
for (const entry of entries) {
|
|
13303
13300
|
if (!entry.isDirectory()) continue;
|
|
13304
13301
|
const fixtureDir = path43.join(root, entry.name);
|
|
13305
13302
|
const reportFile = path43.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
|
|
13306
|
-
if (!
|
|
13303
|
+
if (!existsSync64(reportFile)) continue;
|
|
13307
13304
|
const report = await readFile20(reportFile, "utf8");
|
|
13308
13305
|
rows.push(parseAgentReport(entry.name, report));
|
|
13309
13306
|
}
|
|
@@ -13393,8 +13390,8 @@ function escapeRegExp(value) {
|
|
|
13393
13390
|
}
|
|
13394
13391
|
|
|
13395
13392
|
// src/commands/eval.ts
|
|
13396
|
-
import { mkdir as mkdir19, readFile as readFile21, writeFile as
|
|
13397
|
-
import { existsSync as
|
|
13393
|
+
import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
|
|
13394
|
+
import { existsSync as existsSync65 } from "fs";
|
|
13398
13395
|
import path44 from "path";
|
|
13399
13396
|
import "commander";
|
|
13400
13397
|
import {
|
|
@@ -13422,7 +13419,7 @@ function registerEval(program2) {
|
|
|
13422
13419
|
).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--fail-under-catch-rate <pct>", "exit non-zero if sensor catch-rate is below this percentage").option("--fail-under-gate-precision <pct>", "exit non-zero if gate precision is below this percentage").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("--regression-gate", "CI-safe gate: compare against the baseline IF one exists (fail on regression), else no-op", false).option("--record", "append this run's score to .ai/.cache/eval-history.jsonl (trend the harness over time)", false).option("--trend", "print the recorded score trend (sparkline + latest/best/delta) and exit", false).option("--ref <ref>", "version/commit label stored with a --record run").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
13423
13420
|
const root = findProjectRoot42(opts.dir);
|
|
13424
13421
|
const paths = resolveHaivePaths38(root);
|
|
13425
|
-
if (!
|
|
13422
|
+
if (!existsSync65(paths.memoriesDir)) {
|
|
13426
13423
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
13427
13424
|
process.exitCode = 1;
|
|
13428
13425
|
return;
|
|
@@ -13501,13 +13498,13 @@ function registerEval(program2) {
|
|
|
13501
13498
|
gate_precision: gatePrecision
|
|
13502
13499
|
};
|
|
13503
13500
|
await mkdir19(path44.dirname(baselineFile), { recursive: true });
|
|
13504
|
-
await
|
|
13501
|
+
await writeFile33(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
|
|
13505
13502
|
if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path44.relative(root, baselineFile)}`);
|
|
13506
13503
|
}
|
|
13507
13504
|
let delta = null;
|
|
13508
13505
|
let gateDelta = null;
|
|
13509
13506
|
if (opts.compare || opts.regressionGate) {
|
|
13510
|
-
if (!
|
|
13507
|
+
if (!existsSync65(baselineFile)) {
|
|
13511
13508
|
if (opts.regressionGate) {
|
|
13512
13509
|
if (!opts.json) ui.info(`No baseline at ${path44.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
|
|
13513
13510
|
} else {
|
|
@@ -13545,7 +13542,7 @@ function registerEval(program2) {
|
|
|
13545
13542
|
const md = renderMarkdown2(root, k, resolvedSpec.source, report, gatePrecision);
|
|
13546
13543
|
if (opts.out) {
|
|
13547
13544
|
const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
|
|
13548
|
-
await
|
|
13545
|
+
await writeFile33(outFile, md, "utf8");
|
|
13549
13546
|
ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
|
|
13550
13547
|
} else {
|
|
13551
13548
|
console.log(md);
|
|
@@ -13632,10 +13629,10 @@ async function resolveSpec(opts, root, memoriesDir) {
|
|
|
13632
13629
|
return { spec: JSON.parse(raw), source: file };
|
|
13633
13630
|
}
|
|
13634
13631
|
const defaultSpec = path44.join(root, ".ai", "eval", "spec.json");
|
|
13635
|
-
if (
|
|
13632
|
+
if (existsSync65(defaultSpec)) {
|
|
13636
13633
|
const raw = await readFile21(defaultSpec, "utf8");
|
|
13637
13634
|
const explicit = JSON.parse(raw);
|
|
13638
|
-
const memories2 = await
|
|
13635
|
+
const memories2 = await loadMemoriesFromDir27(memoriesDir);
|
|
13639
13636
|
const synthesized = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
|
|
13640
13637
|
return {
|
|
13641
13638
|
spec: {
|
|
@@ -13645,7 +13642,7 @@ async function resolveSpec(opts, root, memoriesDir) {
|
|
|
13645
13642
|
source: ".ai/eval/spec.json + synthesized anchored retrieval"
|
|
13646
13643
|
};
|
|
13647
13644
|
}
|
|
13648
|
-
const memories = await
|
|
13645
|
+
const memories = await loadMemoriesFromDir27(memoriesDir);
|
|
13649
13646
|
return {
|
|
13650
13647
|
spec: { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) },
|
|
13651
13648
|
source: "synthesized anchored retrieval"
|
|
@@ -13743,8 +13740,8 @@ function renderMarkdown2(root, k, source, report, gatePrecision) {
|
|
|
13743
13740
|
}
|
|
13744
13741
|
|
|
13745
13742
|
// src/commands/memory-suggest.ts
|
|
13746
|
-
import { mkdir as mkdir20, writeFile as
|
|
13747
|
-
import { existsSync as
|
|
13743
|
+
import { mkdir as mkdir20, writeFile as writeFile34 } from "fs/promises";
|
|
13744
|
+
import { existsSync as existsSync66 } from "fs";
|
|
13748
13745
|
import path45 from "path";
|
|
13749
13746
|
import "commander";
|
|
13750
13747
|
import {
|
|
@@ -13752,7 +13749,7 @@ import {
|
|
|
13752
13749
|
buildFrontmatter as buildFrontmatter11,
|
|
13753
13750
|
findProjectRoot as findProjectRoot43,
|
|
13754
13751
|
loadConfig as loadConfig10,
|
|
13755
|
-
loadMemoriesFromDir as
|
|
13752
|
+
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
13756
13753
|
memoryFilePath as memoryFilePath11,
|
|
13757
13754
|
parseSince as parseSince2,
|
|
13758
13755
|
readUsageEvents as readUsageEvents3,
|
|
@@ -13823,7 +13820,7 @@ function registerMemorySuggest(memory2) {
|
|
|
13823
13820
|
}
|
|
13824
13821
|
const created = [];
|
|
13825
13822
|
const skipped = [];
|
|
13826
|
-
const existing =
|
|
13823
|
+
const existing = existsSync66(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
|
|
13827
13824
|
for (const s of top) {
|
|
13828
13825
|
const slug = slugify2(s.query);
|
|
13829
13826
|
if (!slug) {
|
|
@@ -13847,11 +13844,11 @@ function registerMemorySuggest(memory2) {
|
|
|
13847
13844
|
const body = renderTemplate(s, fm.id, status);
|
|
13848
13845
|
const file = memoryFilePath11(paths, fm.scope, fm.id, fm.module);
|
|
13849
13846
|
await mkdir20(path45.dirname(file), { recursive: true });
|
|
13850
|
-
if (
|
|
13847
|
+
if (existsSync66(file)) {
|
|
13851
13848
|
skipped.push({ query: s.query, reason: `file already exists at ${path45.relative(root, file)}` });
|
|
13852
13849
|
continue;
|
|
13853
13850
|
}
|
|
13854
|
-
await
|
|
13851
|
+
await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
|
|
13855
13852
|
created.push({ id: fm.id, file: path45.relative(root, file), query: s.query });
|
|
13856
13853
|
}
|
|
13857
13854
|
if (opts.json) {
|
|
@@ -13950,8 +13947,8 @@ function truncate2(text, max) {
|
|
|
13950
13947
|
}
|
|
13951
13948
|
|
|
13952
13949
|
// src/commands/memory-archive.ts
|
|
13953
|
-
import { existsSync as
|
|
13954
|
-
import { writeFile as
|
|
13950
|
+
import { existsSync as existsSync67 } from "fs";
|
|
13951
|
+
import { writeFile as writeFile35 } from "fs/promises";
|
|
13955
13952
|
import path46 from "path";
|
|
13956
13953
|
import "commander";
|
|
13957
13954
|
import {
|
|
@@ -13959,7 +13956,7 @@ import {
|
|
|
13959
13956
|
getUsage as getUsage21,
|
|
13960
13957
|
retirementSignal as retirementSignal2,
|
|
13961
13958
|
loadConfig as loadConfig11,
|
|
13962
|
-
loadMemoriesFromDir as
|
|
13959
|
+
loadMemoriesFromDir as loadMemoriesFromDir34,
|
|
13963
13960
|
loadUsageIndex as loadUsageIndex29,
|
|
13964
13961
|
resolveHaivePaths as resolveHaivePaths40,
|
|
13965
13962
|
serializeMemory as serializeMemory28
|
|
@@ -13971,7 +13968,7 @@ function registerMemoryArchive(memory2) {
|
|
|
13971
13968
|
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m'). Default: enforcement.decayAfterDays or 180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--unread", "decay by unread-age ALONE (ignore anchor status) \u2014 more aggressive corpus hygiene", false).option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
13972
13969
|
const root = findProjectRoot44(opts.dir);
|
|
13973
13970
|
const paths = resolveHaivePaths40(root);
|
|
13974
|
-
if (!
|
|
13971
|
+
if (!existsSync67(paths.memoriesDir)) {
|
|
13975
13972
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
13976
13973
|
process.exitCode = 1;
|
|
13977
13974
|
return;
|
|
@@ -13985,7 +13982,7 @@ function registerMemoryArchive(memory2) {
|
|
|
13985
13982
|
return;
|
|
13986
13983
|
}
|
|
13987
13984
|
const cutoff = Date.now() - minDays * MS_PER_DAY2;
|
|
13988
|
-
const all = await
|
|
13985
|
+
const all = await loadMemoriesFromDir34(paths.memoriesDir);
|
|
13989
13986
|
const usage = await loadUsageIndex29(paths);
|
|
13990
13987
|
const typeFilter = opts.type === "all" ? null : opts.type ?? "attempt";
|
|
13991
13988
|
const candidates = [];
|
|
@@ -13996,7 +13993,7 @@ function registerMemoryArchive(memory2) {
|
|
|
13996
13993
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
13997
13994
|
const retired = retirementSignal2(fm, mem.body);
|
|
13998
13995
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
13999
|
-
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !
|
|
13996
|
+
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync67(path46.join(paths.root, p)));
|
|
14000
13997
|
const isAnchorless = !hasAnyAnchor;
|
|
14001
13998
|
if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
|
|
14002
13999
|
const u = getUsage21(usage, fm.id);
|
|
@@ -14045,7 +14042,7 @@ function registerMemoryArchive(memory2) {
|
|
|
14045
14042
|
if (!found) continue;
|
|
14046
14043
|
const fm = { ...found.memory.frontmatter, status: "deprecated" };
|
|
14047
14044
|
try {
|
|
14048
|
-
await
|
|
14045
|
+
await writeFile35(c.filePath, serializeMemory28({ frontmatter: fm, body: found.memory.body }), "utf8");
|
|
14049
14046
|
archived++;
|
|
14050
14047
|
} catch (err) {
|
|
14051
14048
|
if (!opts.json) {
|
|
@@ -14071,8 +14068,8 @@ function parseDays(input) {
|
|
|
14071
14068
|
}
|
|
14072
14069
|
|
|
14073
14070
|
// src/commands/doctor.ts
|
|
14074
|
-
import { existsSync as
|
|
14075
|
-
import { readFile as readFile23, stat, writeFile as
|
|
14071
|
+
import { existsSync as existsSync68, statSync as statSync2 } from "fs";
|
|
14072
|
+
import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
|
|
14076
14073
|
import path47 from "path";
|
|
14077
14074
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
14078
14075
|
import "commander";
|
|
@@ -14083,7 +14080,7 @@ import {
|
|
|
14083
14080
|
isStackPackSeed as isStackPackSeed2,
|
|
14084
14081
|
loadCodeMap as loadCodeMap7,
|
|
14085
14082
|
loadConfig as loadConfig12,
|
|
14086
|
-
loadMemoriesFromDir as
|
|
14083
|
+
loadMemoriesFromDir as loadMemoriesFromDir35,
|
|
14087
14084
|
loadUsageIndex as loadUsageIndex30,
|
|
14088
14085
|
readUsageEvents as readUsageEvents4,
|
|
14089
14086
|
resolveHaivePaths as resolveHaivePaths41
|
|
@@ -14098,7 +14095,7 @@ function registerDoctor(program2) {
|
|
|
14098
14095
|
const findings = [];
|
|
14099
14096
|
const repairs = [];
|
|
14100
14097
|
const config = await loadConfig12(paths);
|
|
14101
|
-
if (!
|
|
14098
|
+
if (!existsSync68(paths.haiveDir)) {
|
|
14102
14099
|
findings.push({
|
|
14103
14100
|
severity: "error",
|
|
14104
14101
|
code: "not-initialized",
|
|
@@ -14119,7 +14116,7 @@ function registerDoctor(program2) {
|
|
|
14119
14116
|
})
|
|
14120
14117
|
);
|
|
14121
14118
|
}
|
|
14122
|
-
if (!
|
|
14119
|
+
if (!existsSync68(paths.projectContext)) {
|
|
14123
14120
|
findings.push({
|
|
14124
14121
|
severity: "warn",
|
|
14125
14122
|
code: "no-project-context",
|
|
@@ -14148,7 +14145,7 @@ function registerDoctor(program2) {
|
|
|
14148
14145
|
});
|
|
14149
14146
|
}
|
|
14150
14147
|
}
|
|
14151
|
-
const memories =
|
|
14148
|
+
const memories = existsSync68(paths.memoriesDir) ? await loadMemoriesFromDir35(paths.memoriesDir) : [];
|
|
14152
14149
|
const now = Date.now();
|
|
14153
14150
|
if (memories.length === 0) {
|
|
14154
14151
|
findings.push({
|
|
@@ -14300,7 +14297,7 @@ function registerDoctor(program2) {
|
|
|
14300
14297
|
if (config.enforcement?.requireBriefingFirst) {
|
|
14301
14298
|
const claudeSettings = path47.join(root, ".claude", "settings.local.json");
|
|
14302
14299
|
let hasClaudeEnforcement = false;
|
|
14303
|
-
if (
|
|
14300
|
+
if (existsSync68(claudeSettings)) {
|
|
14304
14301
|
try {
|
|
14305
14302
|
const { readFile: readFile28 } = await import("fs/promises");
|
|
14306
14303
|
const raw = await readFile28(claudeSettings, "utf8");
|
|
@@ -14326,7 +14323,7 @@ function registerDoctor(program2) {
|
|
|
14326
14323
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
14327
14324
|
});
|
|
14328
14325
|
}
|
|
14329
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
14326
|
+
findings.push(...await collectInstallFindings(root, "0.19.0"));
|
|
14330
14327
|
findings.push(...await collectToolchainFindings(root));
|
|
14331
14328
|
try {
|
|
14332
14329
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
@@ -14334,7 +14331,7 @@ function registerDoctor(program2) {
|
|
|
14334
14331
|
timeout: 3e3,
|
|
14335
14332
|
stdio: ["ignore", "pipe", "ignore"]
|
|
14336
14333
|
}).trim();
|
|
14337
|
-
const cliVersion = "0.
|
|
14334
|
+
const cliVersion = "0.19.0";
|
|
14338
14335
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
14339
14336
|
findings.push({
|
|
14340
14337
|
severity: "warn",
|
|
@@ -14356,14 +14353,14 @@ npm uninstall -g @hiveai/mcp`
|
|
|
14356
14353
|
];
|
|
14357
14354
|
const staleConfigs = [];
|
|
14358
14355
|
for (const cfgPath of configPaths) {
|
|
14359
|
-
if (!
|
|
14356
|
+
if (!existsSync68(cfgPath)) continue;
|
|
14360
14357
|
try {
|
|
14361
14358
|
const raw = await readFile23(cfgPath, "utf8");
|
|
14362
14359
|
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
14363
14360
|
staleConfigs.push(path47.relative(root, cfgPath));
|
|
14364
14361
|
if (opts.fix && !opts.dryRun) {
|
|
14365
14362
|
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
14366
|
-
await
|
|
14363
|
+
await writeFile36(cfgPath, updated, "utf8");
|
|
14367
14364
|
}
|
|
14368
14365
|
}
|
|
14369
14366
|
} catch {
|
|
@@ -14652,7 +14649,7 @@ which -a haive`
|
|
|
14652
14649
|
];
|
|
14653
14650
|
for (const rel of integrationFiles) {
|
|
14654
14651
|
const file = path47.join(root, rel);
|
|
14655
|
-
if (!
|
|
14652
|
+
if (!existsSync68(file)) continue;
|
|
14656
14653
|
const text = await readFile23(file, "utf8").catch(() => "");
|
|
14657
14654
|
for (const bin of extractAbsoluteHaiveBins(text)) {
|
|
14658
14655
|
const version = versionForBinary(bin);
|
|
@@ -14700,7 +14697,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
|
14700
14697
|
const isHaiveWorkspace = (await readJson(path47.join(root, "package.json")))?.name === "haive-monorepo";
|
|
14701
14698
|
if (!isHaiveWorkspace) return findings;
|
|
14702
14699
|
const cliDist = path47.join(root, "packages/cli/dist/index.js");
|
|
14703
|
-
if (!
|
|
14700
|
+
if (!existsSync68(cliDist)) {
|
|
14704
14701
|
findings.push({
|
|
14705
14702
|
severity: "warn",
|
|
14706
14703
|
code: "workspace-dist-missing",
|
|
@@ -14724,7 +14721,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
|
14724
14721
|
"packages/core/src/index.ts",
|
|
14725
14722
|
"packages/mcp/src/server.ts",
|
|
14726
14723
|
"packages/cli/src/index.ts"
|
|
14727
|
-
].map((rel) => path47.join(root, rel)).filter(
|
|
14724
|
+
].map((rel) => path47.join(root, rel)).filter(existsSync68);
|
|
14728
14725
|
if (sourceFiles.length > 0) {
|
|
14729
14726
|
const distMtime = statSync2(cliDist).mtimeMs;
|
|
14730
14727
|
const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
|
|
@@ -14813,7 +14810,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
|
|
|
14813
14810
|
}
|
|
14814
14811
|
}
|
|
14815
14812
|
async function readJson(file) {
|
|
14816
|
-
if (!
|
|
14813
|
+
if (!existsSync68(file)) return null;
|
|
14817
14814
|
try {
|
|
14818
14815
|
return JSON.parse(await readFile23(file, "utf8"));
|
|
14819
14816
|
} catch {
|
|
@@ -14884,11 +14881,11 @@ function extractAbsoluteHaiveBins(text) {
|
|
|
14884
14881
|
}
|
|
14885
14882
|
|
|
14886
14883
|
// src/commands/playback.ts
|
|
14887
|
-
import { existsSync as
|
|
14884
|
+
import { existsSync as existsSync69 } from "fs";
|
|
14888
14885
|
import "commander";
|
|
14889
14886
|
import {
|
|
14890
14887
|
findProjectRoot as findProjectRoot46,
|
|
14891
|
-
loadMemoriesFromDir as
|
|
14888
|
+
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
14892
14889
|
parseSince as parseSince3,
|
|
14893
14890
|
readUsageEvents as readUsageEvents5,
|
|
14894
14891
|
resolveHaivePaths as resolveHaivePaths42
|
|
@@ -14914,7 +14911,7 @@ function registerPlayback(program2) {
|
|
|
14914
14911
|
const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
|
|
14915
14912
|
const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
|
|
14916
14913
|
const sessions = bucketSessions(filtered, gapMs);
|
|
14917
|
-
const all =
|
|
14914
|
+
const all = existsSync69(paths.memoriesDir) ? await loadMemoriesFromDir36(paths.memoriesDir) : [];
|
|
14918
14915
|
const memByCreatedAt = all.filter(({ memory: memory2 }) => memory2.frontmatter.type !== "session_recap").map(({ memory: memory2 }) => ({ id: memory2.frontmatter.id, at: Date.parse(memory2.frontmatter.created_at) })).sort((a, b) => a.at - b.at);
|
|
14919
14916
|
const enriched = sessions.map((s, i) => {
|
|
14920
14917
|
const startMs = Date.parse(s.start);
|
|
@@ -15156,11 +15153,11 @@ function runCommand3(cmd, args, cwd) {
|
|
|
15156
15153
|
}
|
|
15157
15154
|
|
|
15158
15155
|
// src/commands/welcome.ts
|
|
15159
|
-
import { existsSync as
|
|
15156
|
+
import { existsSync as existsSync70 } from "fs";
|
|
15160
15157
|
import "commander";
|
|
15161
15158
|
import {
|
|
15162
15159
|
findProjectRoot as findProjectRoot48,
|
|
15163
|
-
loadMemoriesFromDir as
|
|
15160
|
+
loadMemoriesFromDir as loadMemoriesFromDir37,
|
|
15164
15161
|
resolveHaivePaths as resolveHaivePaths44
|
|
15165
15162
|
} from "@hiveai/core";
|
|
15166
15163
|
var TYPE_RANK = {
|
|
@@ -15178,12 +15175,12 @@ function registerWelcome(program2) {
|
|
|
15178
15175
|
).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
15179
15176
|
const root = findProjectRoot48(opts.dir);
|
|
15180
15177
|
const paths = resolveHaivePaths44(root);
|
|
15181
|
-
if (!
|
|
15178
|
+
if (!existsSync70(paths.memoriesDir)) {
|
|
15182
15179
|
ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
|
|
15183
15180
|
process.exitCode = 1;
|
|
15184
15181
|
return;
|
|
15185
15182
|
}
|
|
15186
|
-
const all = await
|
|
15183
|
+
const all = await loadMemoriesFromDir37(paths.memoriesDir);
|
|
15187
15184
|
const team = all.filter(
|
|
15188
15185
|
({ memory: memory2 }) => memory2.frontmatter.scope === "team" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && memory2.frontmatter.status !== "stale" && memory2.frontmatter.type !== "session_recap"
|
|
15189
15186
|
);
|
|
@@ -15269,7 +15266,7 @@ function registerResolveProject(program2) {
|
|
|
15269
15266
|
}
|
|
15270
15267
|
|
|
15271
15268
|
// src/commands/runtime-journal.ts
|
|
15272
|
-
import { existsSync as
|
|
15269
|
+
import { existsSync as existsSync71 } from "fs";
|
|
15273
15270
|
import path49 from "path";
|
|
15274
15271
|
import "commander";
|
|
15275
15272
|
import {
|
|
@@ -15295,7 +15292,7 @@ function registerRuntime(program2) {
|
|
|
15295
15292
|
const root = path49.resolve(opts.dir ?? process.cwd());
|
|
15296
15293
|
const paths = resolveHaivePaths45(findProjectRoot49(root));
|
|
15297
15294
|
const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
15298
|
-
if (!
|
|
15295
|
+
if (!existsSync71(paths.haiveDir)) {
|
|
15299
15296
|
ui.error("No .ai/ \u2014 run `haive init` first.");
|
|
15300
15297
|
process.exitCode = 1;
|
|
15301
15298
|
return;
|
|
@@ -15310,7 +15307,7 @@ function registerRuntime(program2) {
|
|
|
15310
15307
|
}
|
|
15311
15308
|
|
|
15312
15309
|
// src/commands/memory-timeline.ts
|
|
15313
|
-
import { existsSync as
|
|
15310
|
+
import { existsSync as existsSync73 } from "fs";
|
|
15314
15311
|
import path50 from "path";
|
|
15315
15312
|
import "commander";
|
|
15316
15313
|
import {
|
|
@@ -15329,13 +15326,13 @@ function registerMemoryTimeline(memory2) {
|
|
|
15329
15326
|
}
|
|
15330
15327
|
const root = path50.resolve(opts.dir ?? process.cwd());
|
|
15331
15328
|
const paths = resolveHaivePaths46(findProjectRoot50(root));
|
|
15332
|
-
if (!
|
|
15329
|
+
if (!existsSync73(paths.memoriesDir)) {
|
|
15333
15330
|
ui.error("No memories \u2014 run `haive init`.");
|
|
15334
15331
|
process.exitCode = 1;
|
|
15335
15332
|
return;
|
|
15336
15333
|
}
|
|
15337
15334
|
const limit = Math.min(100, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
15338
|
-
const all = await
|
|
15335
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
15339
15336
|
const { entries, notice } = collectTimelineEntries2(all, {
|
|
15340
15337
|
memoryId: opts.id,
|
|
15341
15338
|
topic: opts.topic,
|
|
@@ -15347,7 +15344,7 @@ function registerMemoryTimeline(memory2) {
|
|
|
15347
15344
|
}
|
|
15348
15345
|
|
|
15349
15346
|
// src/commands/memory-conflict-candidates.ts
|
|
15350
|
-
import { existsSync as
|
|
15347
|
+
import { existsSync as existsSync74 } from "fs";
|
|
15351
15348
|
import path51 from "path";
|
|
15352
15349
|
import "commander";
|
|
15353
15350
|
import {
|
|
@@ -15372,7 +15369,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
15372
15369
|
).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
|
|
15373
15370
|
const root = path51.resolve(opts.dir ?? process.cwd());
|
|
15374
15371
|
const paths = resolveHaivePaths47(findProjectRoot51(root));
|
|
15375
|
-
if (!
|
|
15372
|
+
if (!existsSync74(paths.memoriesDir)) {
|
|
15376
15373
|
ui.error("No memories \u2014 run `haive init`.");
|
|
15377
15374
|
process.exitCode = 1;
|
|
15378
15375
|
return;
|
|
@@ -15382,7 +15379,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
15382
15379
|
const maxPairs = Math.min(100, Math.max(1, parseInt(opts.maxPairs, 10) || 20));
|
|
15383
15380
|
const maxScan = Math.min(2e3, Math.max(1, parseInt(opts.maxScan, 10) || 500));
|
|
15384
15381
|
const maxTopicPairs = Math.min(100, Math.max(1, parseInt(opts.maxTopicPairs, 10) || 20));
|
|
15385
|
-
const all = await
|
|
15382
|
+
const all = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
15386
15383
|
const lexical = findLexicalConflictPairs2(all, {
|
|
15387
15384
|
sinceDays,
|
|
15388
15385
|
types: parseTypes(opts.types),
|
|
@@ -15408,8 +15405,8 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
15408
15405
|
|
|
15409
15406
|
// src/commands/enforce.ts
|
|
15410
15407
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
15411
|
-
import { existsSync as
|
|
15412
|
-
import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir6, rm as rm3, writeFile as
|
|
15408
|
+
import { existsSync as existsSync75, statSync as statSync3 } from "fs";
|
|
15409
|
+
import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir6, rm as rm3, writeFile as writeFile37 } from "fs/promises";
|
|
15413
15410
|
import path53 from "path";
|
|
15414
15411
|
import "commander";
|
|
15415
15412
|
import {
|
|
@@ -15419,7 +15416,7 @@ import {
|
|
|
15419
15416
|
hasRecentBriefingMarker as hasRecentBriefingMarker2,
|
|
15420
15417
|
isFreshIsoDate,
|
|
15421
15418
|
loadConfig as loadConfig14,
|
|
15422
|
-
loadMemoriesFromDir as
|
|
15419
|
+
loadMemoriesFromDir as loadMemoriesFromDir38,
|
|
15423
15420
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
15424
15421
|
readRecentBriefingMarker,
|
|
15425
15422
|
resolveBriefingBudget as resolveBriefingBudget3,
|
|
@@ -15484,14 +15481,14 @@ function registerEnforce(program2) {
|
|
|
15484
15481
|
const root = findProjectRoot52(opts.dir);
|
|
15485
15482
|
const paths = resolveHaivePaths48(root);
|
|
15486
15483
|
const cacheDir = path53.join(paths.haiveDir, ".cache");
|
|
15487
|
-
if (
|
|
15484
|
+
if (existsSync75(cacheDir)) {
|
|
15488
15485
|
if (opts.dryRun) ui.info(`would clean ${path53.relative(root, cacheDir)} (preserving .gitignore)`);
|
|
15489
15486
|
else {
|
|
15490
15487
|
const removed = await cleanupCacheDir(cacheDir);
|
|
15491
15488
|
ui.success(`cleaned ${path53.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
15492
15489
|
}
|
|
15493
15490
|
}
|
|
15494
|
-
if (
|
|
15491
|
+
if (existsSync75(paths.runtimeDir)) {
|
|
15495
15492
|
if (opts.dryRun) ui.info(`would clean ${path53.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
|
|
15496
15493
|
else {
|
|
15497
15494
|
const removed = await cleanupRuntimeDir(paths.runtimeDir);
|
|
@@ -15529,7 +15526,7 @@ function registerEnforce(program2) {
|
|
|
15529
15526
|
const root = resolveRoot(opts.dir, payload);
|
|
15530
15527
|
if (!root) return;
|
|
15531
15528
|
const paths = resolveHaivePaths48(root);
|
|
15532
|
-
if (!
|
|
15529
|
+
if (!existsSync75(paths.haiveDir)) return;
|
|
15533
15530
|
await mkdir21(paths.runtimeDir, { recursive: true });
|
|
15534
15531
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
15535
15532
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
|
|
@@ -15592,7 +15589,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
15592
15589
|
const root = resolveRoot(opts.dir, payload);
|
|
15593
15590
|
if (!root) return;
|
|
15594
15591
|
const paths = resolveHaivePaths48(root);
|
|
15595
|
-
if (!
|
|
15592
|
+
if (!existsSync75(paths.haiveDir)) return;
|
|
15596
15593
|
if (!isWriteLikeTool(payload)) return;
|
|
15597
15594
|
const config = await loadConfig14(paths);
|
|
15598
15595
|
if (config.enforcement?.requireBriefingFirst === false) return;
|
|
@@ -15657,7 +15654,7 @@ function emitPreToolUseContext(text) {
|
|
|
15657
15654
|
async function buildFinishReport(dir) {
|
|
15658
15655
|
const root = findProjectRoot52(dir);
|
|
15659
15656
|
const paths = resolveHaivePaths48(root);
|
|
15660
|
-
const initialized =
|
|
15657
|
+
const initialized = existsSync75(paths.haiveDir);
|
|
15661
15658
|
const config = initialized ? await loadConfig14(paths) : {};
|
|
15662
15659
|
const mode = config.enforcement?.mode ?? "strict";
|
|
15663
15660
|
const findings = [];
|
|
@@ -15849,7 +15846,7 @@ async function checkFailureCapture(paths, config) {
|
|
|
15849
15846
|
const gate = config.enforcement?.failureCaptureGate ?? "warn";
|
|
15850
15847
|
if (gate === "off") return [];
|
|
15851
15848
|
const obsFile = path53.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
15852
|
-
if (!
|
|
15849
|
+
if (!existsSync75(obsFile)) return [];
|
|
15853
15850
|
const failures = [];
|
|
15854
15851
|
try {
|
|
15855
15852
|
const raw = await readFile24(obsFile, "utf8");
|
|
@@ -15866,7 +15863,7 @@ async function checkFailureCapture(paths, config) {
|
|
|
15866
15863
|
return [];
|
|
15867
15864
|
}
|
|
15868
15865
|
if (failures.length === 0) return [];
|
|
15869
|
-
const memories =
|
|
15866
|
+
const memories = existsSync75(paths.memoriesDir) ? await loadMemoriesFromDir38(paths.memoriesDir) : [];
|
|
15870
15867
|
const captureTimes = memories.filter(({ memory: memory2 }) => ["attempt", "gotcha"].includes(memory2.frontmatter.type)).map(({ memory: memory2 }) => memory2.frontmatter.created_at);
|
|
15871
15868
|
const uncaptured = findUncapturedFailures(failures, captureTimes);
|
|
15872
15869
|
if (uncaptured.length === 0) {
|
|
@@ -15901,7 +15898,7 @@ function finishReport(root, initialized, mode, findings, config) {
|
|
|
15901
15898
|
async function runWithEnforcement(command, args, opts) {
|
|
15902
15899
|
const root = findProjectRoot52(opts.dir);
|
|
15903
15900
|
const paths = resolveHaivePaths48(root);
|
|
15904
|
-
if (!
|
|
15901
|
+
if (!existsSync75(paths.haiveDir)) {
|
|
15905
15902
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
15906
15903
|
process.exit(1);
|
|
15907
15904
|
}
|
|
@@ -15990,13 +15987,13 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
15990
15987
|
if (briefing.setup_warnings.length > 0) {
|
|
15991
15988
|
parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
|
|
15992
15989
|
}
|
|
15993
|
-
await
|
|
15990
|
+
await writeFile37(file, parts.join("\n") + "\n", "utf8");
|
|
15994
15991
|
return file;
|
|
15995
15992
|
}
|
|
15996
15993
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
15997
15994
|
const root = findProjectRoot52(dir);
|
|
15998
15995
|
const paths = resolveHaivePaths48(root);
|
|
15999
|
-
const initialized =
|
|
15996
|
+
const initialized = existsSync75(paths.haiveDir);
|
|
16000
15997
|
const config = initialized ? await loadConfig14(paths) : {};
|
|
16001
15998
|
if (initialized) {
|
|
16002
15999
|
await applyLightweightRepairs(root, paths);
|
|
@@ -16030,7 +16027,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
16030
16027
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
16031
16028
|
});
|
|
16032
16029
|
}
|
|
16033
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
16030
|
+
findings.push(...await inspectIntegrationVersions(root, "0.19.0"));
|
|
16034
16031
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
16035
16032
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
16036
16033
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -16100,8 +16097,8 @@ function withCategories(report) {
|
|
|
16100
16097
|
};
|
|
16101
16098
|
}
|
|
16102
16099
|
async function hasRecentSessionRecap(paths) {
|
|
16103
|
-
if (!
|
|
16104
|
-
const all = await
|
|
16100
|
+
if (!existsSync75(paths.memoriesDir)) return false;
|
|
16101
|
+
const all = await loadMemoriesFromDir38(paths.memoriesDir);
|
|
16105
16102
|
return all.some(({ memory: memory2 }) => {
|
|
16106
16103
|
const fm = memory2.frontmatter;
|
|
16107
16104
|
const freshnessDate = fm.verified_at ?? fm.created_at;
|
|
@@ -16109,8 +16106,8 @@ async function hasRecentSessionRecap(paths) {
|
|
|
16109
16106
|
});
|
|
16110
16107
|
}
|
|
16111
16108
|
async function verifyMemoryPolicy(paths, config) {
|
|
16112
|
-
if (!
|
|
16113
|
-
const all = await
|
|
16109
|
+
if (!existsSync75(paths.memoriesDir)) return [];
|
|
16110
|
+
const all = await loadMemoriesFromDir38(paths.memoriesDir);
|
|
16114
16111
|
const findings = [];
|
|
16115
16112
|
const staleImportant = [];
|
|
16116
16113
|
let verified = 0;
|
|
@@ -16147,12 +16144,12 @@ async function verifyMemoryPolicy(paths, config) {
|
|
|
16147
16144
|
return findings;
|
|
16148
16145
|
}
|
|
16149
16146
|
async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
16150
|
-
if (!
|
|
16147
|
+
if (!existsSync75(paths.memoriesDir)) return [];
|
|
16151
16148
|
const changedFiles = (await getChangedFiles(paths.root, stage)).filter((f) => !isGeneratedArtifact(f));
|
|
16152
16149
|
if (changedFiles.length === 0) {
|
|
16153
16150
|
return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
|
|
16154
16151
|
}
|
|
16155
|
-
const all = await
|
|
16152
|
+
const all = await loadMemoriesFromDir38(paths.memoriesDir);
|
|
16156
16153
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention"]);
|
|
16157
16154
|
const relevant = all.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
16158
16155
|
const fm = memory2.frontmatter;
|
|
@@ -16267,9 +16264,9 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
16267
16264
|
await rm3(path53.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
16268
16265
|
removed++;
|
|
16269
16266
|
}
|
|
16270
|
-
await
|
|
16271
|
-
if (!
|
|
16272
|
-
await
|
|
16267
|
+
await writeFile37(path53.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
16268
|
+
if (!existsSync75(path53.join(runtimeDir, "README.md"))) {
|
|
16269
|
+
await writeFile37(
|
|
16273
16270
|
path53.join(runtimeDir, "README.md"),
|
|
16274
16271
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
16275
16272
|
"utf8"
|
|
@@ -16286,7 +16283,7 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
16286
16283
|
await rm3(path53.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
16287
16284
|
removed++;
|
|
16288
16285
|
}
|
|
16289
|
-
await
|
|
16286
|
+
await writeFile37(path53.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
16290
16287
|
return removed;
|
|
16291
16288
|
}
|
|
16292
16289
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -16311,7 +16308,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
|
|
|
16311
16308
|
const findings = [];
|
|
16312
16309
|
for (const rel of files) {
|
|
16313
16310
|
const file = path53.join(root, rel);
|
|
16314
|
-
if (!
|
|
16311
|
+
if (!existsSync75(file)) continue;
|
|
16315
16312
|
const text = await readFile24(file, "utf8").catch(() => "");
|
|
16316
16313
|
for (const bin of extractAbsoluteHaiveBins2(text)) {
|
|
16317
16314
|
const version = versionForBinary2(bin);
|
|
@@ -16421,7 +16418,7 @@ async function resolveCiDiffRange(root) {
|
|
|
16421
16418
|
}
|
|
16422
16419
|
async function resolveGithubEventRange(root) {
|
|
16423
16420
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
16424
|
-
if (!eventPath || !
|
|
16421
|
+
if (!eventPath || !existsSync75(eventPath)) return null;
|
|
16425
16422
|
try {
|
|
16426
16423
|
const event = JSON.parse(await readFile24(eventPath, "utf8"));
|
|
16427
16424
|
const prBase = cleanGitSha(event.pull_request?.base?.sha);
|
|
@@ -16744,7 +16741,7 @@ function buildScore(findings, threshold = 80) {
|
|
|
16744
16741
|
}
|
|
16745
16742
|
async function installGitEnforcement(root) {
|
|
16746
16743
|
const hooksDir = path53.join(root, ".git", "hooks");
|
|
16747
|
-
if (!
|
|
16744
|
+
if (!existsSync75(path53.join(root, ".git"))) {
|
|
16748
16745
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
16749
16746
|
return;
|
|
16750
16747
|
}
|
|
@@ -16774,17 +16771,17 @@ haive enforce commit-msg "$1" --dir . || exit $?
|
|
|
16774
16771
|
];
|
|
16775
16772
|
for (const hook of hooks) {
|
|
16776
16773
|
const file = path53.join(hooksDir, hook.name);
|
|
16777
|
-
if (
|
|
16774
|
+
if (existsSync75(file)) {
|
|
16778
16775
|
const current = await readFile24(file, "utf8").catch(() => "");
|
|
16779
16776
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
16780
|
-
await
|
|
16777
|
+
await writeFile37(file, hook.body, "utf8");
|
|
16781
16778
|
} else {
|
|
16782
|
-
await
|
|
16779
|
+
await writeFile37(file, `${current.trimEnd()}
|
|
16783
16780
|
|
|
16784
16781
|
${hook.body}`, "utf8");
|
|
16785
16782
|
}
|
|
16786
16783
|
} else {
|
|
16787
|
-
await
|
|
16784
|
+
await writeFile37(file, hook.body, "utf8");
|
|
16788
16785
|
}
|
|
16789
16786
|
await chmod2(file, 493);
|
|
16790
16787
|
}
|
|
@@ -16793,11 +16790,11 @@ ${hook.body}`, "utf8");
|
|
|
16793
16790
|
async function installCiEnforcement(root) {
|
|
16794
16791
|
const workflowPath = path53.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
16795
16792
|
await mkdir21(path53.dirname(workflowPath), { recursive: true });
|
|
16796
|
-
if (
|
|
16793
|
+
if (existsSync75(workflowPath)) {
|
|
16797
16794
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
16798
16795
|
return;
|
|
16799
16796
|
}
|
|
16800
|
-
await
|
|
16797
|
+
await writeFile37(workflowPath, `name: haive-enforcement
|
|
16801
16798
|
|
|
16802
16799
|
on:
|
|
16803
16800
|
pull_request:
|
|
@@ -16932,11 +16929,11 @@ function normalizeToolPath(file, root) {
|
|
|
16932
16929
|
return path53.relative(root, normalized).replace(/\\/g, "/");
|
|
16933
16930
|
}
|
|
16934
16931
|
async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
|
|
16935
|
-
if (!
|
|
16932
|
+
if (!existsSync75(paths.memoriesDir)) return [];
|
|
16936
16933
|
const marker = await readRecentBriefingMarker(paths, sessionId);
|
|
16937
16934
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
16938
16935
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
|
|
16939
|
-
const all = await
|
|
16936
|
+
const all = await loadMemoriesFromDir38(paths.memoriesDir);
|
|
16940
16937
|
return all.filter(({ memory: memory2 }) => {
|
|
16941
16938
|
const fm = memory2.frontmatter;
|
|
16942
16939
|
if (!policyTypes.has(fm.type)) return false;
|
|
@@ -17017,17 +17014,17 @@ function registerRun(program2) {
|
|
|
17017
17014
|
|
|
17018
17015
|
// src/commands/sensors.ts
|
|
17019
17016
|
import { execFile as execFile3 } from "child_process";
|
|
17020
|
-
import { existsSync as
|
|
17021
|
-
import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as
|
|
17017
|
+
import { existsSync as existsSync76 } from "fs";
|
|
17018
|
+
import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
|
|
17022
17019
|
import path54 from "path";
|
|
17023
17020
|
import { promisify as promisify3 } from "util";
|
|
17024
17021
|
import "commander";
|
|
17025
17022
|
import {
|
|
17026
17023
|
appendPreventionEvent as appendPreventionEvent2,
|
|
17027
17024
|
findProjectRoot as findProjectRoot53,
|
|
17028
|
-
isRetiredMemory as
|
|
17025
|
+
isRetiredMemory as isRetiredMemory3,
|
|
17029
17026
|
loadConfig as loadConfig15,
|
|
17030
|
-
loadMemoriesFromDir as
|
|
17027
|
+
loadMemoriesFromDir as loadMemoriesFromDir39,
|
|
17031
17028
|
loadUsageIndex as loadUsageIndex31,
|
|
17032
17029
|
recordPrevention as recordPrevention2,
|
|
17033
17030
|
resolveHaivePaths as resolveHaivePaths49,
|
|
@@ -17157,7 +17154,7 @@ function registerSensors(program2) {
|
|
|
17157
17154
|
}
|
|
17158
17155
|
const root = findProjectRoot53(opts.dir);
|
|
17159
17156
|
const paths = resolveHaivePaths49(root);
|
|
17160
|
-
const loaded =
|
|
17157
|
+
const loaded = existsSync76(paths.memoriesDir) ? await loadMemoriesFromDir39(paths.memoriesDir) : [];
|
|
17161
17158
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
17162
17159
|
if (!found) {
|
|
17163
17160
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -17177,7 +17174,7 @@ function registerSensors(program2) {
|
|
|
17177
17174
|
},
|
|
17178
17175
|
body: found.memory.body
|
|
17179
17176
|
};
|
|
17180
|
-
await
|
|
17177
|
+
await writeFile38(found.filePath, serializeMemory29(next), "utf8");
|
|
17181
17178
|
ui.success(`Updated ${id}: sensor severity=${severity}`);
|
|
17182
17179
|
if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
|
|
17183
17180
|
ui.info(`message=${sensor.message}`);
|
|
@@ -17196,7 +17193,7 @@ function registerSensors(program2) {
|
|
|
17196
17193
|
await mkdir23(outDir, { recursive: true });
|
|
17197
17194
|
const outPath = path54.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
|
|
17198
17195
|
const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
|
|
17199
|
-
await
|
|
17196
|
+
await writeFile38(outPath, content, "utf8");
|
|
17200
17197
|
if (format === "grep") await chmod3(outPath, 493);
|
|
17201
17198
|
ui.success(`Exported ${rows.length} sensor(s): ${path54.relative(root, outPath)}`);
|
|
17202
17199
|
});
|
|
@@ -17219,13 +17216,13 @@ async function sensorRows(paths) {
|
|
|
17219
17216
|
});
|
|
17220
17217
|
}
|
|
17221
17218
|
async function runnableSensorMemories(paths, regexOnly = true) {
|
|
17222
|
-
if (!
|
|
17223
|
-
const loaded = await
|
|
17219
|
+
if (!existsSync76(paths.memoriesDir)) return [];
|
|
17220
|
+
const loaded = await loadMemoriesFromDir39(paths.memoriesDir);
|
|
17224
17221
|
return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
17225
17222
|
const sensor = memory2.frontmatter.sensor;
|
|
17226
17223
|
if (!sensor) return false;
|
|
17227
17224
|
if (regexOnly && sensor.kind !== "regex") return false;
|
|
17228
|
-
return !
|
|
17225
|
+
return !isRetiredMemory3(memory2.frontmatter, memory2.body);
|
|
17229
17226
|
});
|
|
17230
17227
|
}
|
|
17231
17228
|
async function runCommandSensor(spec, root) {
|
|
@@ -17269,15 +17266,15 @@ function shellQuote(value) {
|
|
|
17269
17266
|
}
|
|
17270
17267
|
|
|
17271
17268
|
// src/commands/ingest.ts
|
|
17272
|
-
import { existsSync as
|
|
17273
|
-
import { mkdir as mkdir24, readFile as readFile26, writeFile as
|
|
17269
|
+
import { existsSync as existsSync77 } from "fs";
|
|
17270
|
+
import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
|
|
17274
17271
|
import path55 from "path";
|
|
17275
17272
|
import "commander";
|
|
17276
17273
|
import {
|
|
17277
17274
|
draftsFromFindings as draftsFromFindings2,
|
|
17278
17275
|
filterNewDrafts as filterNewDrafts2,
|
|
17279
17276
|
findProjectRoot as findProjectRoot54,
|
|
17280
|
-
loadMemoriesFromDir as
|
|
17277
|
+
loadMemoriesFromDir as loadMemoriesFromDir40,
|
|
17281
17278
|
memoryFilePath as memoryFilePath12,
|
|
17282
17279
|
parseFindings as parseFindings2,
|
|
17283
17280
|
resolveHaivePaths as resolveHaivePaths50,
|
|
@@ -17307,7 +17304,7 @@ function registerIngest(program2) {
|
|
|
17307
17304
|
}
|
|
17308
17305
|
const root = findProjectRoot54(opts.dir);
|
|
17309
17306
|
const paths = resolveHaivePaths50(root);
|
|
17310
|
-
if (!
|
|
17307
|
+
if (!existsSync77(paths.haiveDir)) {
|
|
17311
17308
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
17312
17309
|
process.exitCode = 1;
|
|
17313
17310
|
return;
|
|
@@ -17329,7 +17326,7 @@ function registerIngest(program2) {
|
|
|
17329
17326
|
return;
|
|
17330
17327
|
}
|
|
17331
17328
|
const reportPath = path55.resolve(root, file);
|
|
17332
|
-
if (!
|
|
17329
|
+
if (!existsSync77(reportPath)) {
|
|
17333
17330
|
ui.error(`Report file not found: ${reportPath}`);
|
|
17334
17331
|
process.exitCode = 1;
|
|
17335
17332
|
return;
|
|
@@ -17358,7 +17355,7 @@ function registerIngest(program2) {
|
|
|
17358
17355
|
process.exitCode = 1;
|
|
17359
17356
|
return;
|
|
17360
17357
|
}
|
|
17361
|
-
const existing =
|
|
17358
|
+
const existing = existsSync77(paths.memoriesDir) ? await loadMemoriesFromDir40(paths.memoriesDir) : [];
|
|
17362
17359
|
const existingTopics = new Set(
|
|
17363
17360
|
existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
|
|
17364
17361
|
);
|
|
@@ -17424,7 +17421,7 @@ function registerIngest(program2) {
|
|
|
17424
17421
|
async function writeDraft2(paths, draft) {
|
|
17425
17422
|
const file = memoryFilePath12(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
|
|
17426
17423
|
await mkdir24(path55.dirname(file), { recursive: true });
|
|
17427
|
-
await
|
|
17424
|
+
await writeFile39(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
17428
17425
|
return file;
|
|
17429
17426
|
}
|
|
17430
17427
|
async function fetchSonarIssues(opts) {
|
|
@@ -17465,13 +17462,13 @@ async function fetchSonarIssues(opts) {
|
|
|
17465
17462
|
}
|
|
17466
17463
|
|
|
17467
17464
|
// src/commands/dashboard.ts
|
|
17468
|
-
import { existsSync as
|
|
17465
|
+
import { existsSync as existsSync78 } from "fs";
|
|
17469
17466
|
import "commander";
|
|
17470
17467
|
import {
|
|
17471
17468
|
buildDashboard,
|
|
17472
17469
|
findProjectRoot as findProjectRoot55,
|
|
17473
17470
|
loadConfig as loadConfig16,
|
|
17474
|
-
loadMemoriesFromDir as
|
|
17471
|
+
loadMemoriesFromDir as loadMemoriesFromDir41,
|
|
17475
17472
|
loadPreventionEvents as loadPreventionEvents4,
|
|
17476
17473
|
loadUsageIndex as loadUsageIndex33,
|
|
17477
17474
|
resolveHaivePaths as resolveHaivePaths51
|
|
@@ -17482,12 +17479,12 @@ function registerDashboard(program2) {
|
|
|
17482
17479
|
).option("--json", "emit the full report as JSON", false).option("--top <n>", "rows per top-list", "10").option("--dormant-days <n>", "dormancy window for impact scoring").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
17483
17480
|
const root = findProjectRoot55(opts.dir);
|
|
17484
17481
|
const paths = resolveHaivePaths51(root);
|
|
17485
|
-
if (!
|
|
17482
|
+
if (!existsSync78(paths.haiveDir)) {
|
|
17486
17483
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
17487
17484
|
process.exitCode = 1;
|
|
17488
17485
|
return;
|
|
17489
17486
|
}
|
|
17490
|
-
const memories =
|
|
17487
|
+
const memories = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
|
|
17491
17488
|
const usage = await loadUsageIndex33(paths);
|
|
17492
17489
|
const preventionEvents = await loadPreventionEvents4(paths);
|
|
17493
17490
|
const config = await loadConfig16(paths);
|
|
@@ -17601,7 +17598,7 @@ function warnNum(n) {
|
|
|
17601
17598
|
// src/commands/dev-link.ts
|
|
17602
17599
|
import { execFile as execFile4 } from "child_process";
|
|
17603
17600
|
import { cp, readFile as readFile27 } from "fs/promises";
|
|
17604
|
-
import { existsSync as
|
|
17601
|
+
import { existsSync as existsSync79 } from "fs";
|
|
17605
17602
|
import path56 from "path";
|
|
17606
17603
|
import { promisify as promisify4 } from "util";
|
|
17607
17604
|
import "commander";
|
|
@@ -17611,7 +17608,7 @@ function registerDevLink(program2) {
|
|
|
17611
17608
|
const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on hAIve itself.");
|
|
17612
17609
|
dev.command("link").description("Hot-swap this repo's built dist into the global @hiveai install so `haive` runs your local code.").option("-d, --dir <dir>", "repo root (default: discovered from cwd)").option("--json", "emit a machine-readable summary", false).action(async (opts) => {
|
|
17613
17610
|
const root = findProjectRoot56(opts.dir);
|
|
17614
|
-
if (!
|
|
17611
|
+
if (!existsSync79(path56.join(root, "packages", "cli", "dist", "index.js"))) {
|
|
17615
17612
|
ui.error(`Not the hAIve monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
|
|
17616
17613
|
process.exitCode = 1;
|
|
17617
17614
|
return;
|
|
@@ -17623,7 +17620,7 @@ function registerDevLink(program2) {
|
|
|
17623
17620
|
globalModules = path56.join(path56.dirname(path56.dirname(process.execPath)), "lib", "node_modules");
|
|
17624
17621
|
}
|
|
17625
17622
|
const globalHive = path56.join(globalModules, "@hiveai");
|
|
17626
|
-
if (!
|
|
17623
|
+
if (!existsSync79(globalHive)) {
|
|
17627
17624
|
ui.error(`No global @hiveai install at ${globalHive}. Install once with \`npm i -g @hiveai/cli\`, then re-run.`);
|
|
17628
17625
|
process.exitCode = 1;
|
|
17629
17626
|
return;
|
|
@@ -17631,7 +17628,7 @@ function registerDevLink(program2) {
|
|
|
17631
17628
|
const linked = [];
|
|
17632
17629
|
const copyDist = async (fromPkg, toDistDir) => {
|
|
17633
17630
|
const from = path56.join(root, "packages", fromPkg, "dist");
|
|
17634
|
-
if (!
|
|
17631
|
+
if (!existsSync79(from) || !existsSync79(path56.dirname(toDistDir))) return;
|
|
17635
17632
|
await cp(from, toDistDir, { recursive: true });
|
|
17636
17633
|
linked.push(path56.relative(globalModules, toDistDir));
|
|
17637
17634
|
};
|
|
@@ -17690,7 +17687,7 @@ function registerCoverage(program2) {
|
|
|
17690
17687
|
maxHotFiles: 500
|
|
17691
17688
|
});
|
|
17692
17689
|
const hotFiles = radar.hotFiles.filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes }));
|
|
17693
|
-
const memories = await
|
|
17690
|
+
const memories = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
17694
17691
|
const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
|
|
17695
17692
|
if (opts.json) {
|
|
17696
17693
|
console.log(JSON.stringify({ root, scanned_hot_files: hotFiles.length, gaps }, null, 2));
|
|
@@ -17718,7 +17715,7 @@ function registerCoverage(program2) {
|
|
|
17718
17715
|
|
|
17719
17716
|
// src/commands/merge-driver.ts
|
|
17720
17717
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
17721
|
-
import { readFileSync, writeFileSync, existsSync as
|
|
17718
|
+
import { readFileSync, writeFileSync, existsSync as existsSync80 } from "fs";
|
|
17722
17719
|
import path57 from "path";
|
|
17723
17720
|
import "commander";
|
|
17724
17721
|
import { findProjectRoot as findProjectRoot58, mergeMemoryVersions } from "@hiveai/core";
|
|
@@ -17752,7 +17749,7 @@ function registerMergeDriver(program2) {
|
|
|
17752
17749
|
return;
|
|
17753
17750
|
}
|
|
17754
17751
|
const gaPath = path57.join(root, ".gitattributes");
|
|
17755
|
-
let content =
|
|
17752
|
+
let content = existsSync80(gaPath) ? readFileSync(gaPath, "utf8") : "";
|
|
17756
17753
|
if (!content.includes(GITATTRIBUTES_MARK)) {
|
|
17757
17754
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
17758
17755
|
content += GITATTRIBUTES_BLOCK + "\n";
|
|
@@ -17766,8 +17763,8 @@ function registerMergeDriver(program2) {
|
|
|
17766
17763
|
}
|
|
17767
17764
|
|
|
17768
17765
|
// src/commands/memory-resolve-conflict.ts
|
|
17769
|
-
import { writeFile as
|
|
17770
|
-
import { existsSync as
|
|
17766
|
+
import { writeFile as writeFile40 } from "fs/promises";
|
|
17767
|
+
import { existsSync as existsSync81 } from "fs";
|
|
17771
17768
|
import "commander";
|
|
17772
17769
|
import {
|
|
17773
17770
|
findProjectRoot as findProjectRoot59,
|
|
@@ -17779,12 +17776,12 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
17779
17776
|
memory2.command("resolve-conflict <id_a> <id_b>").description("Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other").option("--yes", "apply the resolution (without this, only previews it)", false).option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (idA, idB, opts) => {
|
|
17780
17777
|
const root = findProjectRoot59(opts.dir);
|
|
17781
17778
|
const paths = resolveHaivePaths53(root);
|
|
17782
|
-
if (!
|
|
17779
|
+
if (!existsSync81(paths.memoriesDir)) {
|
|
17783
17780
|
ui.error(`No .ai/memories at ${root}.`);
|
|
17784
17781
|
process.exitCode = 1;
|
|
17785
17782
|
return;
|
|
17786
17783
|
}
|
|
17787
|
-
const memories = await
|
|
17784
|
+
const memories = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
17788
17785
|
const a = memories.find((m) => m.memory.frontmatter.id === idA);
|
|
17789
17786
|
const b = memories.find((m) => m.memory.frontmatter.id === idB);
|
|
17790
17787
|
if (!a || !b) {
|
|
@@ -17806,7 +17803,7 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
17806
17803
|
if (!opts.json) ui.info("Preview only \u2014 re-run with --yes to apply.");
|
|
17807
17804
|
return;
|
|
17808
17805
|
}
|
|
17809
|
-
await
|
|
17806
|
+
await writeFile40(
|
|
17810
17807
|
loser.filePath,
|
|
17811
17808
|
serializeMemory31({
|
|
17812
17809
|
frontmatter: {
|
|
@@ -17825,8 +17822,8 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
17825
17822
|
|
|
17826
17823
|
// src/commands/memory-seed-git.ts
|
|
17827
17824
|
import { execFile as execFile5 } from "child_process";
|
|
17828
|
-
import { mkdir as mkdir25, writeFile as
|
|
17829
|
-
import { existsSync as
|
|
17825
|
+
import { mkdir as mkdir25, writeFile as writeFile41 } from "fs/promises";
|
|
17826
|
+
import { existsSync as existsSync83 } from "fs";
|
|
17830
17827
|
import path58 from "path";
|
|
17831
17828
|
import { promisify as promisify5 } from "util";
|
|
17832
17829
|
import "commander";
|
|
@@ -17843,7 +17840,7 @@ function registerMemorySeedGit(memory2) {
|
|
|
17843
17840
|
memory2.command("seed-git").description("Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)").option("--apply", "write the proposed seeds as draft memories (default: preview only)", false).option("--limit <n>", "max seeds to propose", "20").option("--days <n>", "git-history lookback window in days", "365").option("--scope <scope>", "personal | team", "team").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
17844
17841
|
const root = findProjectRoot60(opts.dir);
|
|
17845
17842
|
const paths = resolveHaivePaths54(root);
|
|
17846
|
-
if (!
|
|
17843
|
+
if (!existsSync83(paths.haiveDir)) {
|
|
17847
17844
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
17848
17845
|
process.exitCode = 1;
|
|
17849
17846
|
return;
|
|
@@ -17888,9 +17885,9 @@ function registerMemorySeedGit(memory2) {
|
|
|
17888
17885
|
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
17889
17886
|
`;
|
|
17890
17887
|
const file = memoryFilePath13(paths, fm.scope, fm.id, fm.module);
|
|
17891
|
-
if (
|
|
17888
|
+
if (existsSync83(file)) continue;
|
|
17892
17889
|
await mkdir25(path58.dirname(file), { recursive: true });
|
|
17893
|
-
await
|
|
17890
|
+
await writeFile41(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
|
|
17894
17891
|
written += 1;
|
|
17895
17892
|
}
|
|
17896
17893
|
if (!opts.json) {
|
|
@@ -17921,21 +17918,21 @@ async function readCommits(root, days) {
|
|
|
17921
17918
|
}
|
|
17922
17919
|
|
|
17923
17920
|
// src/commands/bridges.ts
|
|
17924
|
-
import { existsSync as
|
|
17921
|
+
import { existsSync as existsSync84 } from "fs";
|
|
17925
17922
|
import path59 from "path";
|
|
17926
17923
|
import "commander";
|
|
17927
17924
|
import {
|
|
17928
17925
|
findProjectRoot as findProjectRoot61,
|
|
17929
17926
|
resolveHaivePaths as resolveHaivePaths55,
|
|
17930
17927
|
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
|
|
17931
|
-
BRIDGE_TARGETS as
|
|
17928
|
+
BRIDGE_TARGETS as BRIDGE_TARGETS3
|
|
17932
17929
|
} from "@hiveai/core";
|
|
17933
17930
|
function registerBridges(program2) {
|
|
17934
17931
|
const bridges = program2.command("bridges").description(
|
|
17935
17932
|
"Generate native agent bridge files from the hAIve corpus.\n Bridges inject top validated memories and block sensors into agent-harness-specific\n config files (.cursor/rules/haive-memories.mdc, .clinerules, .windsurfrules,\n .continuerules, .sourcegraph/cody-rules.md, .rules, AGENTS.md,\n .github/copilot-instructions.md).\n This is the reach differentiator vs memories.sh: our bridges carry enforcement, not just injection.\n\n Example:\n haive bridges sync --all\n haive bridges sync --only cline,windsurf\n"
|
|
17936
17933
|
);
|
|
17937
17934
|
bridges.command("sync").description(
|
|
17938
|
-
"Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " +
|
|
17935
|
+
"Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS3.join(", ") + "\n"
|
|
17939
17936
|
).option("--all", "generate all supported bridge targets").option(
|
|
17940
17937
|
"--only <targets>",
|
|
17941
17938
|
"comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
|
|
@@ -17943,7 +17940,7 @@ function registerBridges(program2) {
|
|
|
17943
17940
|
const root = findProjectRoot61(opts.dir);
|
|
17944
17941
|
const paths = resolveHaivePaths55(root);
|
|
17945
17942
|
const dryRun = opts.dryRun === true;
|
|
17946
|
-
if (!
|
|
17943
|
+
if (!existsSync84(paths.memoriesDir)) {
|
|
17947
17944
|
ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
17948
17945
|
process.exitCode = 1;
|
|
17949
17946
|
return;
|
|
@@ -17951,18 +17948,18 @@ function registerBridges(program2) {
|
|
|
17951
17948
|
let targets;
|
|
17952
17949
|
if (opts.only) {
|
|
17953
17950
|
const requested = opts.only.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
|
|
17954
|
-
const invalid = requested.filter((t) => !
|
|
17951
|
+
const invalid = requested.filter((t) => !BRIDGE_TARGETS3.includes(t));
|
|
17955
17952
|
if (invalid.length > 0) {
|
|
17956
|
-
ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${
|
|
17953
|
+
ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS3.join(", ")}`);
|
|
17957
17954
|
process.exitCode = 1;
|
|
17958
17955
|
return;
|
|
17959
17956
|
}
|
|
17960
17957
|
targets = requested;
|
|
17961
17958
|
} else if (opts.all) {
|
|
17962
|
-
targets =
|
|
17959
|
+
targets = BRIDGE_TARGETS3;
|
|
17963
17960
|
} else {
|
|
17964
|
-
targets =
|
|
17965
|
-
(t) =>
|
|
17961
|
+
targets = BRIDGE_TARGETS3.filter(
|
|
17962
|
+
(t) => existsSync84(path59.join(root, BRIDGE_TARGET_PATH2[t]))
|
|
17966
17963
|
);
|
|
17967
17964
|
if (targets.length === 0) {
|
|
17968
17965
|
ui.info(
|
|
@@ -17989,9 +17986,9 @@ function registerBridges(program2) {
|
|
|
17989
17986
|
bridges.command("list").description("List bridge targets and their status in this project").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
17990
17987
|
const root = findProjectRoot61(opts.dir);
|
|
17991
17988
|
console.log(ui.bold("hAIve bridge targets:"));
|
|
17992
|
-
for (const target of
|
|
17989
|
+
for (const target of BRIDGE_TARGETS3) {
|
|
17993
17990
|
const relPath = BRIDGE_TARGET_PATH2[target];
|
|
17994
|
-
const exists =
|
|
17991
|
+
const exists = existsSync84(path59.join(root, relPath));
|
|
17995
17992
|
const marker = exists ? ui.dim("\u2713") : ui.dim("\xB7");
|
|
17996
17993
|
console.log(` ${marker} ${target.padEnd(10)} ${relPath}${exists ? "" : " (not present)"}`);
|
|
17997
17994
|
}
|
|
@@ -18002,7 +17999,7 @@ function registerBridges(program2) {
|
|
|
18002
17999
|
|
|
18003
18000
|
// src/index.ts
|
|
18004
18001
|
var program = new Command64();
|
|
18005
|
-
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.
|
|
18002
|
+
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.19.0").option("--advanced", "show maintenance and experimental commands in help");
|
|
18006
18003
|
registerInit(program);
|
|
18007
18004
|
registerWelcome(program);
|
|
18008
18005
|
registerResolveProject(program);
|