@hivelore/cli 0.33.0 → 0.34.1
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/{chunk-XDB7EKL4.js → chunk-X6UHROFL.js} +5 -5
- package/dist/chunk-X6UHROFL.js.map +1 -0
- package/dist/index.js +618 -510
- package/dist/index.js.map +1 -1
- package/dist/{server-LZKYO5O5.js → server-FQ5C43MV.js} +2 -2
- package/package.json +4 -4
- package/dist/chunk-XDB7EKL4.js.map +0 -1
- /package/dist/{server-LZKYO5O5.js.map → server-FQ5C43MV.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
preCommitCheck,
|
|
11
11
|
readPresumedCorrectTargets,
|
|
12
12
|
runHaiveMcpStdio
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-X6UHROFL.js";
|
|
14
14
|
import {
|
|
15
15
|
registerMemoryPending
|
|
16
16
|
} from "./chunk-OYJKHD22.js";
|
|
@@ -208,7 +208,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
|
|
|
208
208
|
if (!f) continue;
|
|
209
209
|
counts.set(f, (counts.get(f) ?? 0) + 1);
|
|
210
210
|
}
|
|
211
|
-
let entries = [...counts.entries()].map(([
|
|
211
|
+
let entries = [...counts.entries()].map(([path47, changes]) => ({ path: path47, changes }));
|
|
212
212
|
const lowerPaths = filePaths.map((p) => p.toLowerCase());
|
|
213
213
|
if (lowerPaths.length > 0) {
|
|
214
214
|
entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
|
|
@@ -322,9 +322,9 @@ async function lintMemoriesAsync(root, options = {}) {
|
|
|
322
322
|
message: "Reads like generic best practice a capable model already follows. Hivelore's value is UNGUESSABLE team knowledge \u2014 add the concrete, arbitrary specifics (exact names, values, formats, magic numbers) or consider removing it to keep briefings high-signal."
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
if (ANCHOR_TYPES.has(fm.type) && fm.anchor.paths.length === 0 && fm.status === "validated" && !
|
|
325
|
+
const isStackSeed = fm.tags.includes("stack-pack");
|
|
326
|
+
const suggestedAnchors = isStackSeed ? { paths: [], symbols: [] } : suggestAnchors(root, { filePath, memory: memory2 }, codeMap, trackedFiles);
|
|
327
|
+
if (ANCHOR_TYPES.has(fm.type) && fm.anchor.paths.length === 0 && fm.status === "validated" && !isStackSeed) {
|
|
328
328
|
out.push({
|
|
329
329
|
file: filePath,
|
|
330
330
|
id: fm.id,
|
|
@@ -728,20 +728,7 @@ async function projectContextVersionStatus(root, paths) {
|
|
|
728
728
|
async function refreshCodeMap(root, paths, force) {
|
|
729
729
|
const existing = await loadCodeMap2(paths);
|
|
730
730
|
if (existing && !force) return false;
|
|
731
|
-
const map = await buildCodeMap(root, {
|
|
732
|
-
includeUntracked: true,
|
|
733
|
-
excludeDirs: [
|
|
734
|
-
"node_modules",
|
|
735
|
-
"dist",
|
|
736
|
-
"build",
|
|
737
|
-
"out",
|
|
738
|
-
".git",
|
|
739
|
-
".next",
|
|
740
|
-
".turbo",
|
|
741
|
-
".vitest-cache",
|
|
742
|
-
"coverage"
|
|
743
|
-
]
|
|
744
|
-
});
|
|
731
|
+
const map = await buildCodeMap(root, { includeUntracked: true });
|
|
745
732
|
if (existing && existing.root === map.root && JSON.stringify(existing.files) === JSON.stringify(map.files)) {
|
|
746
733
|
return false;
|
|
747
734
|
}
|
|
@@ -1355,6 +1342,7 @@ import path6 from "path";
|
|
|
1355
1342
|
import "commander";
|
|
1356
1343
|
import {
|
|
1357
1344
|
buildCodeMap as buildCodeMap2,
|
|
1345
|
+
CODE_MAP_DEFAULT_EXCLUDE,
|
|
1358
1346
|
codeMapPath,
|
|
1359
1347
|
findProjectRoot as findProjectRoot4,
|
|
1360
1348
|
loadCodeMap as loadCodeMap4,
|
|
@@ -1383,18 +1371,7 @@ function registerIndexCode(program2) {
|
|
|
1383
1371
|
ui.info(`Indexing source files in ${root}\u2026`);
|
|
1384
1372
|
const map = await buildCodeMap2(root, {
|
|
1385
1373
|
includeUntracked: true,
|
|
1386
|
-
excludeDirs: [
|
|
1387
|
-
"node_modules",
|
|
1388
|
-
"dist",
|
|
1389
|
-
"build",
|
|
1390
|
-
"out",
|
|
1391
|
-
".git",
|
|
1392
|
-
".next",
|
|
1393
|
-
".turbo",
|
|
1394
|
-
".vitest-cache",
|
|
1395
|
-
"coverage",
|
|
1396
|
-
...extraExcludes
|
|
1397
|
-
]
|
|
1374
|
+
excludeDirs: [...CODE_MAP_DEFAULT_EXCLUDE, ...extraExcludes]
|
|
1398
1375
|
});
|
|
1399
1376
|
await saveCodeMap2(paths, map);
|
|
1400
1377
|
const fileCount = Object.keys(map.files).length;
|
|
@@ -1504,14 +1481,14 @@ function isCodeMapStale(root, generatedAt, files) {
|
|
|
1504
1481
|
// src/commands/init.ts
|
|
1505
1482
|
import { execFile as execFile2 } from "child_process";
|
|
1506
1483
|
import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
|
|
1507
|
-
import { existsSync as
|
|
1508
|
-
import
|
|
1484
|
+
import { existsSync as existsSync12 } from "fs";
|
|
1485
|
+
import path13 from "path";
|
|
1509
1486
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1510
1487
|
import { promisify as promisify2 } from "util";
|
|
1511
1488
|
import "commander";
|
|
1512
1489
|
import {
|
|
1513
1490
|
AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
|
|
1514
|
-
BRIDGE_TARGETS,
|
|
1491
|
+
BRIDGE_TARGETS as BRIDGE_TARGETS2,
|
|
1515
1492
|
buildCodeMap as buildCodeMap3,
|
|
1516
1493
|
buildFrontmatter as buildFrontmatter2,
|
|
1517
1494
|
detectStacksFromManifests,
|
|
@@ -1523,20 +1500,93 @@ import {
|
|
|
1523
1500
|
serializeMemory as serializeMemory3
|
|
1524
1501
|
} from "@hivelore/core";
|
|
1525
1502
|
|
|
1503
|
+
// src/utils/doc-files.ts
|
|
1504
|
+
import { readdirSync } from "fs";
|
|
1505
|
+
function findDocFile(root, stem) {
|
|
1506
|
+
let entries;
|
|
1507
|
+
try {
|
|
1508
|
+
entries = readdirSync(root);
|
|
1509
|
+
} catch {
|
|
1510
|
+
return void 0;
|
|
1511
|
+
}
|
|
1512
|
+
const re = new RegExp(`^${stem}(\\.(md|markdown|txt|rst))?$`, "i");
|
|
1513
|
+
return entries.find((e) => re.test(e));
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// src/utils/bridge-detect.ts
|
|
1517
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
|
|
1518
|
+
import { homedir } from "os";
|
|
1519
|
+
import path7 from "path";
|
|
1520
|
+
import { BRIDGE_TARGET_PATH, BRIDGE_TARGETS } from "@hivelore/core";
|
|
1521
|
+
var VSCODE_EXTENSION_SIGNALS = {
|
|
1522
|
+
copilot: ["github.copilot"],
|
|
1523
|
+
cline: ["saoudrizwan.claude-dev"],
|
|
1524
|
+
roo: ["rooveterinaryinc.roo"],
|
|
1525
|
+
cody: ["sourcegraph.cody"],
|
|
1526
|
+
continue: ["continue.continue"]
|
|
1527
|
+
};
|
|
1528
|
+
var HOME_SIGNALS = {
|
|
1529
|
+
claude: [".claude", ".claude.json"],
|
|
1530
|
+
cursor: [".cursor"],
|
|
1531
|
+
windsurf: [".codeium/windsurf", ".windsurf"],
|
|
1532
|
+
gemini: [".gemini"],
|
|
1533
|
+
continue: [".continue"],
|
|
1534
|
+
aider: [".aider.conf.yml", ".aider"],
|
|
1535
|
+
zed: [".config/zed", "Library/Application Support/Zed"],
|
|
1536
|
+
copilot: [".config/github-copilot"]
|
|
1537
|
+
};
|
|
1538
|
+
var ENV_SIGNALS = {
|
|
1539
|
+
claude: ["CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"],
|
|
1540
|
+
cursor: ["CURSOR_AGENT"],
|
|
1541
|
+
gemini: ["GEMINI_CLI"],
|
|
1542
|
+
aider: ["AIDER_MODEL"]
|
|
1543
|
+
};
|
|
1544
|
+
function vscodeExtensionIds(home) {
|
|
1545
|
+
const ids = [];
|
|
1546
|
+
for (const dir of [".vscode/extensions", ".vscode-server/extensions", ".vscode-oss/extensions"]) {
|
|
1547
|
+
try {
|
|
1548
|
+
ids.push(...readdirSync2(path7.join(home, dir)).map((e) => e.toLowerCase()));
|
|
1549
|
+
} catch {
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return ids;
|
|
1553
|
+
}
|
|
1554
|
+
function detectBridgeTargets(root, env = process.env, home = homedir()) {
|
|
1555
|
+
const reasons = { agents: "universal" };
|
|
1556
|
+
const extensions = vscodeExtensionIds(home);
|
|
1557
|
+
for (const target of BRIDGE_TARGETS) {
|
|
1558
|
+
if (target === "agents") continue;
|
|
1559
|
+
if (existsSync6(path7.join(root, BRIDGE_TARGET_PATH[target]))) {
|
|
1560
|
+
reasons[target] = "repo file";
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
const homeHit = (HOME_SIGNALS[target] ?? []).some((p) => existsSync6(path7.join(home, p)));
|
|
1564
|
+
const envHit = (ENV_SIGNALS[target] ?? []).some((v) => env[v]);
|
|
1565
|
+
const extHit = (VSCODE_EXTENSION_SIGNALS[target] ?? []).some(
|
|
1566
|
+
(prefix) => extensions.some((id) => id.startsWith(prefix))
|
|
1567
|
+
);
|
|
1568
|
+
if (homeHit || envHit || extHit) reasons[target] = "installed";
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
targets: BRIDGE_TARGETS.filter((t) => t in reasons),
|
|
1572
|
+
reasons
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1526
1576
|
// src/utils/bridge-files.ts
|
|
1527
1577
|
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
1528
|
-
import { existsSync as
|
|
1529
|
-
import
|
|
1578
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1579
|
+
import path8 from "path";
|
|
1530
1580
|
import {
|
|
1531
1581
|
BRIDGE_MARKERS,
|
|
1532
|
-
BRIDGE_TARGET_PATH,
|
|
1582
|
+
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
|
|
1533
1583
|
generateBridges,
|
|
1534
1584
|
isRetiredMemory,
|
|
1535
1585
|
loadMemoriesFromDir as loadMemoriesFromDir5
|
|
1536
1586
|
} from "@hivelore/core";
|
|
1537
1587
|
async function writeBridgeFiles(root, paths, opts) {
|
|
1538
1588
|
const result = { created: [], updated: [], unchanged: [], skipped: [], warnings: [] };
|
|
1539
|
-
if (!
|
|
1589
|
+
if (!existsSync7(paths.memoriesDir)) return result;
|
|
1540
1590
|
const allLoaded = await loadMemoriesFromDir5(paths.memoriesDir);
|
|
1541
1591
|
const memories = allLoaded.map((l) => l.memory).filter((m) => !isRetiredMemory(m.frontmatter, m.body));
|
|
1542
1592
|
const sensors = [];
|
|
@@ -1554,8 +1604,8 @@ async function writeBridgeFiles(root, paths, opts) {
|
|
|
1554
1604
|
const maxMemories = Math.max(1, opts.maxMemories ?? 8);
|
|
1555
1605
|
const outputs = generateBridges(memories, sensors, { maxMemories, targets: opts.targets });
|
|
1556
1606
|
for (const output of outputs) {
|
|
1557
|
-
const targetFile =
|
|
1558
|
-
const fileExists =
|
|
1607
|
+
const targetFile = path8.join(root, output.path);
|
|
1608
|
+
const fileExists = existsSync7(targetFile);
|
|
1559
1609
|
if (opts.onlyExisting && !fileExists) continue;
|
|
1560
1610
|
if (opts.dryRun) {
|
|
1561
1611
|
if (!fileExists) {
|
|
@@ -1574,7 +1624,7 @@ async function writeBridgeFiles(root, paths, opts) {
|
|
|
1574
1624
|
}
|
|
1575
1625
|
continue;
|
|
1576
1626
|
}
|
|
1577
|
-
await mkdir2(
|
|
1627
|
+
await mkdir2(path8.dirname(targetFile), { recursive: true });
|
|
1578
1628
|
if (!fileExists) {
|
|
1579
1629
|
await writeFile3(targetFile, output.content, "utf8");
|
|
1580
1630
|
result.created.push(output.path);
|
|
@@ -1597,10 +1647,10 @@ async function writeBridgeFiles(root, paths, opts) {
|
|
|
1597
1647
|
return result;
|
|
1598
1648
|
}
|
|
1599
1649
|
async function getBridgeFileStatuses(root, paths, opts) {
|
|
1600
|
-
if (!
|
|
1650
|
+
if (!existsSync7(paths.memoriesDir)) {
|
|
1601
1651
|
return opts.targets.map((target) => ({
|
|
1602
1652
|
target,
|
|
1603
|
-
path:
|
|
1653
|
+
path: BRIDGE_TARGET_PATH2[target],
|
|
1604
1654
|
exists: false,
|
|
1605
1655
|
state: "missing",
|
|
1606
1656
|
wouldChange: false,
|
|
@@ -1627,8 +1677,8 @@ async function getBridgeFileStatuses(root, paths, opts) {
|
|
|
1627
1677
|
});
|
|
1628
1678
|
const statuses = [];
|
|
1629
1679
|
for (const output of outputs) {
|
|
1630
|
-
const targetFile =
|
|
1631
|
-
const exists =
|
|
1680
|
+
const targetFile = path8.join(root, output.path);
|
|
1681
|
+
const exists = existsSync7(targetFile);
|
|
1632
1682
|
if (!exists) {
|
|
1633
1683
|
statuses.push({
|
|
1634
1684
|
target: output.target,
|
|
@@ -1784,18 +1834,18 @@ function legacyManagedRange(text) {
|
|
|
1784
1834
|
|
|
1785
1835
|
// src/commands/agent.ts
|
|
1786
1836
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1787
|
-
import { existsSync as
|
|
1837
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1788
1838
|
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
1789
1839
|
import os2 from "os";
|
|
1790
|
-
import
|
|
1840
|
+
import path10 from "path";
|
|
1791
1841
|
import { createInterface } from "readline/promises";
|
|
1792
1842
|
import "commander";
|
|
1793
1843
|
import { findProjectRoot as findProjectRoot5, resolveHaivePaths as resolveHaivePaths5 } from "@hivelore/core";
|
|
1794
1844
|
|
|
1795
1845
|
// src/commands/init-mcp-setup.ts
|
|
1796
1846
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1797
|
-
import { existsSync as
|
|
1798
|
-
import
|
|
1847
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1848
|
+
import path9 from "path";
|
|
1799
1849
|
import os from "os";
|
|
1800
1850
|
var HOME = os.homedir();
|
|
1801
1851
|
var HAIVE_MCP_ENTRY = {
|
|
@@ -1810,14 +1860,14 @@ function projectMcpEntry(root) {
|
|
|
1810
1860
|
};
|
|
1811
1861
|
}
|
|
1812
1862
|
function cursorMcpPath() {
|
|
1813
|
-
return
|
|
1863
|
+
return path9.join(HOME, ".cursor", "mcp.json");
|
|
1814
1864
|
}
|
|
1815
1865
|
async function configureCursor() {
|
|
1816
1866
|
const mcpPath = cursorMcpPath();
|
|
1817
|
-
const cursorDir =
|
|
1818
|
-
if (!
|
|
1867
|
+
const cursorDir = path9.join(HOME, ".cursor");
|
|
1868
|
+
if (!existsSync8(cursorDir)) return { client: "Cursor", status: "not_installed" };
|
|
1819
1869
|
let config = {};
|
|
1820
|
-
if (
|
|
1870
|
+
if (existsSync8(mcpPath)) {
|
|
1821
1871
|
try {
|
|
1822
1872
|
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1823
1873
|
} catch {
|
|
@@ -1832,16 +1882,16 @@ async function configureCursor() {
|
|
|
1832
1882
|
}
|
|
1833
1883
|
function vscodeMcpPath() {
|
|
1834
1884
|
const candidates = [
|
|
1835
|
-
|
|
1885
|
+
path9.join(HOME, ".config", "Code", "User", "mcp.json"),
|
|
1836
1886
|
// Linux
|
|
1837
|
-
|
|
1887
|
+
path9.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
|
|
1838
1888
|
// macOS
|
|
1839
|
-
|
|
1889
|
+
path9.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
|
|
1840
1890
|
// Windows
|
|
1841
|
-
|
|
1891
|
+
path9.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
|
|
1842
1892
|
];
|
|
1843
1893
|
for (const c of candidates) {
|
|
1844
|
-
if (
|
|
1894
|
+
if (existsSync8(path9.dirname(c))) return c;
|
|
1845
1895
|
}
|
|
1846
1896
|
return null;
|
|
1847
1897
|
}
|
|
@@ -1849,7 +1899,7 @@ async function configureVSCode() {
|
|
|
1849
1899
|
const mcpPath = vscodeMcpPath();
|
|
1850
1900
|
if (!mcpPath) return { client: "VS Code", status: "not_installed" };
|
|
1851
1901
|
let config = {};
|
|
1852
|
-
if (
|
|
1902
|
+
if (existsSync8(mcpPath)) {
|
|
1853
1903
|
try {
|
|
1854
1904
|
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1855
1905
|
} catch {
|
|
@@ -1858,24 +1908,24 @@ async function configureVSCode() {
|
|
|
1858
1908
|
config.servers ??= {};
|
|
1859
1909
|
if (config.servers["hivelore"] || config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
|
|
1860
1910
|
config.servers["hivelore"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
|
|
1861
|
-
await mkdir3(
|
|
1911
|
+
await mkdir3(path9.dirname(mcpPath), { recursive: true });
|
|
1862
1912
|
await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
|
|
1863
1913
|
return { client: "VS Code", status: "configured", path: mcpPath };
|
|
1864
1914
|
}
|
|
1865
1915
|
function claudeConfigPath() {
|
|
1866
|
-
const p =
|
|
1867
|
-
if (
|
|
1868
|
-
const p2 =
|
|
1869
|
-
if (
|
|
1916
|
+
const p = path9.join(HOME, ".claude.json");
|
|
1917
|
+
if (existsSync8(p)) return p;
|
|
1918
|
+
const p2 = path9.join(HOME, ".config", "claude", "claude.json");
|
|
1919
|
+
if (existsSync8(path9.dirname(p2))) return p2;
|
|
1870
1920
|
return null;
|
|
1871
1921
|
}
|
|
1872
1922
|
async function configureClaude() {
|
|
1873
|
-
const cfgPath = claudeConfigPath() ??
|
|
1874
|
-
if (!
|
|
1923
|
+
const cfgPath = claudeConfigPath() ?? path9.join(HOME, ".claude.json");
|
|
1924
|
+
if (!existsSync8(cfgPath) && !existsSync8(path9.join(HOME, ".claude"))) {
|
|
1875
1925
|
return { client: "Claude Code", status: "not_installed" };
|
|
1876
1926
|
}
|
|
1877
1927
|
let config = {};
|
|
1878
|
-
if (
|
|
1928
|
+
if (existsSync8(cfgPath)) {
|
|
1879
1929
|
try {
|
|
1880
1930
|
config = JSON.parse(await readFile4(cfgPath, "utf8"));
|
|
1881
1931
|
} catch {
|
|
@@ -1889,11 +1939,11 @@ async function configureClaude() {
|
|
|
1889
1939
|
}
|
|
1890
1940
|
function windsurfMcpPath() {
|
|
1891
1941
|
const candidates = [
|
|
1892
|
-
|
|
1893
|
-
|
|
1942
|
+
path9.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
|
|
1943
|
+
path9.join(HOME, ".windsurf", "mcp.json")
|
|
1894
1944
|
];
|
|
1895
1945
|
for (const c of candidates) {
|
|
1896
|
-
if (
|
|
1946
|
+
if (existsSync8(path9.dirname(c))) return c;
|
|
1897
1947
|
}
|
|
1898
1948
|
return null;
|
|
1899
1949
|
}
|
|
@@ -1901,7 +1951,7 @@ async function configureWindsurf() {
|
|
|
1901
1951
|
const mcpPath = windsurfMcpPath();
|
|
1902
1952
|
if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
|
|
1903
1953
|
let config = {};
|
|
1904
|
-
if (
|
|
1954
|
+
if (existsSync8(mcpPath)) {
|
|
1905
1955
|
try {
|
|
1906
1956
|
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1907
1957
|
} catch {
|
|
@@ -1910,7 +1960,7 @@ async function configureWindsurf() {
|
|
|
1910
1960
|
config.mcpServers ??= {};
|
|
1911
1961
|
if (config.mcpServers["hivelore"] || config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
|
|
1912
1962
|
config.mcpServers["hivelore"] = HAIVE_MCP_ENTRY;
|
|
1913
|
-
await mkdir3(
|
|
1963
|
+
await mkdir3(path9.dirname(mcpPath), { recursive: true });
|
|
1914
1964
|
await writeFile4(mcpPath, JSON.stringify(config, null, 2), "utf8");
|
|
1915
1965
|
return { client: "Windsurf", status: "configured", path: mcpPath };
|
|
1916
1966
|
}
|
|
@@ -1931,9 +1981,9 @@ async function configureProjectMcpClients(root) {
|
|
|
1931
1981
|
const entry = projectMcpEntry(root);
|
|
1932
1982
|
const results = [];
|
|
1933
1983
|
try {
|
|
1934
|
-
const cursorPath =
|
|
1984
|
+
const cursorPath = path9.join(root, ".cursor", "mcp.json");
|
|
1935
1985
|
let config = {};
|
|
1936
|
-
if (
|
|
1986
|
+
if (existsSync8(cursorPath)) {
|
|
1937
1987
|
try {
|
|
1938
1988
|
config = JSON.parse(await readFile4(cursorPath, "utf8"));
|
|
1939
1989
|
} catch {
|
|
@@ -1942,16 +1992,16 @@ async function configureProjectMcpClients(root) {
|
|
|
1942
1992
|
config.mcpServers ??= {};
|
|
1943
1993
|
delete config.mcpServers["haive"];
|
|
1944
1994
|
config.mcpServers["hivelore"] = entry;
|
|
1945
|
-
await mkdir3(
|
|
1995
|
+
await mkdir3(path9.dirname(cursorPath), { recursive: true });
|
|
1946
1996
|
await writeFile4(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1947
1997
|
results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
|
|
1948
1998
|
} catch (err) {
|
|
1949
1999
|
results.push({ client: "Cursor (project)", status: "error", error: String(err) });
|
|
1950
2000
|
}
|
|
1951
2001
|
try {
|
|
1952
|
-
const vscodePath =
|
|
2002
|
+
const vscodePath = path9.join(root, ".vscode", "mcp.json");
|
|
1953
2003
|
let config = {};
|
|
1954
|
-
if (
|
|
2004
|
+
if (existsSync8(vscodePath)) {
|
|
1955
2005
|
try {
|
|
1956
2006
|
config = JSON.parse(await readFile4(vscodePath, "utf8"));
|
|
1957
2007
|
} catch {
|
|
@@ -1960,16 +2010,16 @@ async function configureProjectMcpClients(root) {
|
|
|
1960
2010
|
config.servers ??= {};
|
|
1961
2011
|
delete config.servers["haive"];
|
|
1962
2012
|
config.servers["hivelore"] = { ...entry, type: "stdio" };
|
|
1963
|
-
await mkdir3(
|
|
2013
|
+
await mkdir3(path9.dirname(vscodePath), { recursive: true });
|
|
1964
2014
|
await writeFile4(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1965
2015
|
results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
|
|
1966
2016
|
} catch (err) {
|
|
1967
2017
|
results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
|
|
1968
2018
|
}
|
|
1969
2019
|
try {
|
|
1970
|
-
const mcpPath =
|
|
2020
|
+
const mcpPath = path9.join(root, ".mcp.json");
|
|
1971
2021
|
let config = {};
|
|
1972
|
-
if (
|
|
2022
|
+
if (existsSync8(mcpPath)) {
|
|
1973
2023
|
try {
|
|
1974
2024
|
config = JSON.parse(await readFile4(mcpPath, "utf8"));
|
|
1975
2025
|
} catch {
|
|
@@ -2044,9 +2094,9 @@ async function detectAgentMode(dir) {
|
|
|
2044
2094
|
const root = findProjectRoot5(dir);
|
|
2045
2095
|
const paths = resolveHaivePaths5(root);
|
|
2046
2096
|
const projectMcp = [
|
|
2047
|
-
{ client: "Claude Code", path:
|
|
2048
|
-
{ client: "Cursor", path:
|
|
2049
|
-
{ client: "VS Code", path:
|
|
2097
|
+
{ client: "Claude Code", path: path10.join(root, ".mcp.json"), present: existsSync9(path10.join(root, ".mcp.json")) },
|
|
2098
|
+
{ client: "Cursor", path: path10.join(root, ".cursor", "mcp.json"), present: existsSync9(path10.join(root, ".cursor", "mcp.json")) },
|
|
2099
|
+
{ client: "VS Code", path: path10.join(root, ".vscode", "mcp.json"), present: existsSync9(path10.join(root, ".vscode", "mcp.json")) }
|
|
2050
2100
|
];
|
|
2051
2101
|
const installedAgents = [
|
|
2052
2102
|
{ agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
|
|
@@ -2061,7 +2111,7 @@ async function detectAgentMode(dir) {
|
|
|
2061
2111
|
const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `hivelore run -- ${wrapperAgent.command}` : 'hivelore briefing --task "..." --files "..."';
|
|
2062
2112
|
return {
|
|
2063
2113
|
root,
|
|
2064
|
-
initialized:
|
|
2114
|
+
initialized: existsSync9(paths.haiveDir),
|
|
2065
2115
|
project_mcp: projectMcp,
|
|
2066
2116
|
installed_agents: installedAgents,
|
|
2067
2117
|
recommended_mode: recommendedMode,
|
|
@@ -2069,9 +2119,9 @@ async function detectAgentMode(dir) {
|
|
|
2069
2119
|
};
|
|
2070
2120
|
}
|
|
2071
2121
|
async function writeAgentModeRecord(paths, detection, skippedReason) {
|
|
2072
|
-
const dir =
|
|
2122
|
+
const dir = path10.join(paths.runtimeDir, "enforcement");
|
|
2073
2123
|
await mkdir4(dir, { recursive: true });
|
|
2074
|
-
const file =
|
|
2124
|
+
const file = path10.join(dir, "agent-mode.json");
|
|
2075
2125
|
const record = {
|
|
2076
2126
|
selected_mode: detection.recommended_mode,
|
|
2077
2127
|
recommended_command: detection.recommended_command,
|
|
@@ -2112,7 +2162,7 @@ async function configureCodexIfAvailable(root) {
|
|
|
2112
2162
|
"mcp",
|
|
2113
2163
|
"--stdio"
|
|
2114
2164
|
], { encoding: "utf8" });
|
|
2115
|
-
if (result.status === 0) return { client: "Codex", status: "configured", path:
|
|
2165
|
+
if (result.status === 0) return { client: "Codex", status: "configured", path: path10.join(os2.homedir(), ".codex", "config.toml") };
|
|
2116
2166
|
return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
|
|
2117
2167
|
}
|
|
2118
2168
|
function commandExists(command) {
|
|
@@ -2139,7 +2189,7 @@ function printDetection(detection, json) {
|
|
|
2139
2189
|
console.log(ui.dim(` root: ${detection.root}`));
|
|
2140
2190
|
console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
|
|
2141
2191
|
for (const cfg of detection.project_mcp) {
|
|
2142
|
-
console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(
|
|
2192
|
+
console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path10.relative(detection.root, cfg.path))}`);
|
|
2143
2193
|
}
|
|
2144
2194
|
for (const agent of detection.installed_agents) {
|
|
2145
2195
|
const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
|
|
@@ -2168,8 +2218,8 @@ function printSetupResult(result) {
|
|
|
2168
2218
|
|
|
2169
2219
|
// src/commands/init-bootstrap.ts
|
|
2170
2220
|
import { readdir, readFile as readFile5 } from "fs/promises";
|
|
2171
|
-
import { existsSync as
|
|
2172
|
-
import
|
|
2221
|
+
import { existsSync as existsSync10, readdirSync as readdirSync3 } from "fs";
|
|
2222
|
+
import path11 from "path";
|
|
2173
2223
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
2174
2224
|
"node_modules",
|
|
2175
2225
|
"dist",
|
|
@@ -2255,12 +2305,12 @@ function detectKeyDeps(allDeps) {
|
|
|
2255
2305
|
return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
|
|
2256
2306
|
}
|
|
2257
2307
|
function detectLanguage(root) {
|
|
2258
|
-
if (
|
|
2259
|
-
if (
|
|
2260
|
-
if (
|
|
2261
|
-
if (
|
|
2262
|
-
if (
|
|
2263
|
-
if (
|
|
2308
|
+
if (existsSync10(path11.join(root, "tsconfig.json"))) return "TypeScript";
|
|
2309
|
+
if (existsSync10(path11.join(root, "pyproject.toml")) || existsSync10(path11.join(root, "setup.py"))) return "Python";
|
|
2310
|
+
if (existsSync10(path11.join(root, "go.mod"))) return "Go";
|
|
2311
|
+
if (existsSync10(path11.join(root, "pom.xml")) || existsSync10(path11.join(root, "build.gradle"))) return "Java/Kotlin";
|
|
2312
|
+
if (existsSync10(path11.join(root, "Cargo.toml"))) return "Rust";
|
|
2313
|
+
if (existsSync10(path11.join(root, "package.json"))) {
|
|
2264
2314
|
return hasSourceWithExt(root, [".ts", ".tsx", ".mts", ".cts"]) ? "TypeScript" : "JavaScript";
|
|
2265
2315
|
}
|
|
2266
2316
|
return "Unknown";
|
|
@@ -2270,7 +2320,7 @@ function hasSourceWithExt(root, exts) {
|
|
|
2270
2320
|
const scanDir = (dir, depth) => {
|
|
2271
2321
|
let entries;
|
|
2272
2322
|
try {
|
|
2273
|
-
entries =
|
|
2323
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2274
2324
|
} catch {
|
|
2275
2325
|
return false;
|
|
2276
2326
|
}
|
|
@@ -2280,7 +2330,7 @@ function hasSourceWithExt(root, exts) {
|
|
|
2280
2330
|
if (depth <= 0) return false;
|
|
2281
2331
|
for (const entry of entries) {
|
|
2282
2332
|
if (entry.isDirectory() && !IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
2283
|
-
if (scanDir(
|
|
2333
|
+
if (scanDir(path11.join(dir, entry.name), depth - 1)) return true;
|
|
2284
2334
|
}
|
|
2285
2335
|
}
|
|
2286
2336
|
return false;
|
|
@@ -2301,7 +2351,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
|
|
|
2301
2351
|
if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
|
|
2302
2352
|
if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
|
|
2303
2353
|
if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
|
|
2304
|
-
if (
|
|
2354
|
+
if (existsSync10("pom.xml")) return "Java backend";
|
|
2305
2355
|
return "Application";
|
|
2306
2356
|
}
|
|
2307
2357
|
async function scanDirs(root, maxDepth = 2) {
|
|
@@ -2317,9 +2367,9 @@ async function scanDirs(root, maxDepth = 2) {
|
|
|
2317
2367
|
for (const entry of entries) {
|
|
2318
2368
|
if (!entry.isDirectory()) continue;
|
|
2319
2369
|
if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
2320
|
-
const rel =
|
|
2370
|
+
const rel = path11.relative(root, path11.join(dir, entry.name));
|
|
2321
2371
|
results.push(rel);
|
|
2322
|
-
await walk(
|
|
2372
|
+
await walk(path11.join(dir, entry.name), depth + 1);
|
|
2323
2373
|
}
|
|
2324
2374
|
}
|
|
2325
2375
|
await walk(root, 0);
|
|
@@ -2436,8 +2486,8 @@ function readmeExcerpt(readme) {
|
|
|
2436
2486
|
}
|
|
2437
2487
|
async function generateBootstrapContext(root) {
|
|
2438
2488
|
let pkg = {};
|
|
2439
|
-
const pkgPath =
|
|
2440
|
-
if (
|
|
2489
|
+
const pkgPath = path11.join(root, "package.json");
|
|
2490
|
+
if (existsSync10(pkgPath)) {
|
|
2441
2491
|
try {
|
|
2442
2492
|
pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
2443
2493
|
} catch {
|
|
@@ -2449,18 +2499,15 @@ async function generateBootstrapContext(root) {
|
|
|
2449
2499
|
const language = detectLanguage(root);
|
|
2450
2500
|
const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
|
|
2451
2501
|
const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
|
|
2452
|
-
const projectName = pkg.name ??
|
|
2502
|
+
const projectName = pkg.name ?? path11.basename(root);
|
|
2453
2503
|
const projectDesc = pkg.description ?? "";
|
|
2454
2504
|
let readmeSummary = "";
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
break;
|
|
2462
|
-
} catch {
|
|
2463
|
-
}
|
|
2505
|
+
const readmeName = findDocFile(root, "readme");
|
|
2506
|
+
if (readmeName) {
|
|
2507
|
+
try {
|
|
2508
|
+
const content = await readFile5(path11.join(root, readmeName), "utf8");
|
|
2509
|
+
readmeSummary = readmeExcerpt(content);
|
|
2510
|
+
} catch {
|
|
2464
2511
|
}
|
|
2465
2512
|
}
|
|
2466
2513
|
const dirs = await scanDirs(root, 2);
|
|
@@ -2513,7 +2560,7 @@ async function generateBootstrapContext(root) {
|
|
|
2513
2560
|
"",
|
|
2514
2561
|
`## Gotchas`,
|
|
2515
2562
|
`TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.`,
|
|
2516
|
-
`(
|
|
2563
|
+
`(Seed these with \`hivelore memory import --from ${findDocFile(root, "changelog") ?? "CHANGELOG.md"} --changelog\` or \`hivelore memory import --from ${findDocFile(root, "readme") ?? "README.md"}\` via your AI client.)`,
|
|
2517
2564
|
""
|
|
2518
2565
|
];
|
|
2519
2566
|
return lines.join("\n");
|
|
@@ -2521,8 +2568,8 @@ async function generateBootstrapContext(root) {
|
|
|
2521
2568
|
|
|
2522
2569
|
// src/commands/init-stack-packs.ts
|
|
2523
2570
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
2524
|
-
import { existsSync as
|
|
2525
|
-
import
|
|
2571
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2572
|
+
import path12 from "path";
|
|
2526
2573
|
import {
|
|
2527
2574
|
buildFrontmatter,
|
|
2528
2575
|
loadMemoriesFromDir as loadMemoriesFromDir6,
|
|
@@ -3626,7 +3673,7 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3626
3673
|
const existingTopics = /* @__PURE__ */ new Set();
|
|
3627
3674
|
const existingSignatures = /* @__PURE__ */ new Set();
|
|
3628
3675
|
const handWrittenSlugs = [];
|
|
3629
|
-
if (
|
|
3676
|
+
if (existsSync11(haivePaths.memoriesDir)) {
|
|
3630
3677
|
for (const { memory: memory2 } of await loadMemoriesFromDir6(haivePaths.memoriesDir)) {
|
|
3631
3678
|
if (memory2.frontmatter.topic) existingTopics.add(memory2.frontmatter.topic);
|
|
3632
3679
|
existingSignatures.add(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
|
|
@@ -3671,7 +3718,7 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3671
3718
|
...sensor ? { sensor } : {}
|
|
3672
3719
|
});
|
|
3673
3720
|
const filePath = memoryFilePath(haivePaths, "team", fm.id);
|
|
3674
|
-
if (
|
|
3721
|
+
if (existsSync11(filePath)) continue;
|
|
3675
3722
|
const ruleSlug = combinedSlug.startsWith(`${stack}-`) ? combinedSlug.slice(stack.length + 1) : combinedSlug;
|
|
3676
3723
|
const titleCase = (s) => s.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3677
3724
|
const heading = `${titleCase(stack)}: ${titleCase(ruleSlug)}`;
|
|
@@ -3681,7 +3728,7 @@ ${mem.body}`;
|
|
|
3681
3728
|
const content = serializeMemory2({ frontmatter: fm, body: `${titledBody}
|
|
3682
3729
|
|
|
3683
3730
|
${SEED_FOOTER(stack)}` });
|
|
3684
|
-
await mkdir5(
|
|
3731
|
+
await mkdir5(path12.dirname(filePath), { recursive: true });
|
|
3685
3732
|
await writeFile6(filePath, content, "utf8");
|
|
3686
3733
|
existingTopics.add(topic);
|
|
3687
3734
|
existingSignatures.add(signature);
|
|
@@ -3708,7 +3755,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3708
3755
|
|
|
3709
3756
|
// src/commands/init.ts
|
|
3710
3757
|
var execFileAsync = promisify2(execFile2);
|
|
3711
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3758
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.34.1"}`;
|
|
3712
3759
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3713
3760
|
|
|
3714
3761
|
> Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -3901,9 +3948,9 @@ function registerInit(program2) {
|
|
|
3901
3948
|
"Initialize a Hivelore 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."
|
|
3902
3949
|
).option("-d, --dir <dir>", "project root", process.cwd()).option("--no-bridges", "do not generate any native agent bridge files").option(
|
|
3903
3950
|
"--bridge-targets <list>",
|
|
3904
|
-
`which agent bridges to generate: '
|
|
3905
|
-
Available: ${
|
|
3906
|
-
"
|
|
3951
|
+
`which agent bridges to generate: 'auto' (default \u2014 clients detected on this machine/repo, plus AGENTS.md) | 'all' | comma-list.
|
|
3952
|
+
Available: ${BRIDGE_TARGETS2.join(", ")}. Each carries top memories + block sensors.`,
|
|
3953
|
+
"auto"
|
|
3907
3954
|
).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
|
|
3908
3955
|
"--manual",
|
|
3909
3956
|
"opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
|
|
@@ -3936,7 +3983,7 @@ function registerInit(program2) {
|
|
|
3936
3983
|
"approve user-level AI client configuration prompts during agent setup",
|
|
3937
3984
|
false
|
|
3938
3985
|
).option("--json", "emit a machine-readable summary on stdout (human logs go to stderr)", false).action(async (opts) => {
|
|
3939
|
-
const root =
|
|
3986
|
+
const root = path13.resolve(opts.dir);
|
|
3940
3987
|
const paths = resolveHaivePaths6(root);
|
|
3941
3988
|
const autopilot = opts.manual !== true;
|
|
3942
3989
|
const json = opts.json === true;
|
|
@@ -3953,9 +4000,10 @@ function registerInit(program2) {
|
|
|
3953
4000
|
gitCommitsScanned: 0,
|
|
3954
4001
|
gitRevertsFound: 0,
|
|
3955
4002
|
gitRecurring: 0,
|
|
3956
|
-
bridgesWritten: 0
|
|
4003
|
+
bridgesWritten: 0,
|
|
4004
|
+
bridgeTargets: []
|
|
3957
4005
|
};
|
|
3958
|
-
if (
|
|
4006
|
+
if (existsSync12(paths.haiveDir)) {
|
|
3959
4007
|
ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
|
|
3960
4008
|
}
|
|
3961
4009
|
await mkdir6(paths.personalDir, { recursive: true });
|
|
@@ -3963,8 +4011,8 @@ function registerInit(program2) {
|
|
|
3963
4011
|
await mkdir6(paths.moduleDir, { recursive: true });
|
|
3964
4012
|
await mkdir6(paths.modulesContextDir, { recursive: true });
|
|
3965
4013
|
await ensureAiRuntimeLayout(paths.runtimeDir);
|
|
3966
|
-
await ensureAiCacheLayout(
|
|
3967
|
-
if (!
|
|
4014
|
+
await ensureAiCacheLayout(path13.join(paths.haiveDir, ".cache"));
|
|
4015
|
+
if (!existsSync12(paths.projectContext)) {
|
|
3968
4016
|
if (wantBootstrap) {
|
|
3969
4017
|
ui.info("Bootstrapping project context from local files\u2026");
|
|
3970
4018
|
try {
|
|
@@ -3977,11 +4025,11 @@ function registerInit(program2) {
|
|
|
3977
4025
|
}
|
|
3978
4026
|
} else {
|
|
3979
4027
|
await writeFile7(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
3980
|
-
ui.success(`Created ${
|
|
4028
|
+
ui.success(`Created ${path13.relative(root, paths.projectContext)}`);
|
|
3981
4029
|
}
|
|
3982
4030
|
}
|
|
3983
|
-
const configExists =
|
|
3984
|
-
|
|
4031
|
+
const configExists = existsSync12(
|
|
4032
|
+
path13.join(paths.haiveDir, "haive.config.json")
|
|
3985
4033
|
);
|
|
3986
4034
|
if (!configExists) {
|
|
3987
4035
|
await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
|
|
@@ -4031,11 +4079,14 @@ function registerInit(program2) {
|
|
|
4031
4079
|
}
|
|
4032
4080
|
}
|
|
4033
4081
|
if (opts.bridges) {
|
|
4034
|
-
const targets = resolveBridgeTargets(opts.bridgeTargets);
|
|
4082
|
+
const targets = resolveBridgeTargets(opts.bridgeTargets, root);
|
|
4035
4083
|
const res = await writeBridgeFiles(root, paths, { targets });
|
|
4036
|
-
|
|
4084
|
+
if (targets.includes("cursor")) {
|
|
4085
|
+
await writeCursorHaiveRule(root);
|
|
4086
|
+
}
|
|
4037
4087
|
const made = res.created.length + res.updated.length;
|
|
4038
4088
|
report.bridgesWritten = made;
|
|
4089
|
+
report.bridgeTargets = targets;
|
|
4039
4090
|
if (res.created.length > 0) {
|
|
4040
4091
|
ui.success(`Generated ${res.created.length} agent bridge(s): ${res.created.join(", ")}`);
|
|
4041
4092
|
}
|
|
@@ -4048,13 +4099,13 @@ function registerInit(program2) {
|
|
|
4048
4099
|
}
|
|
4049
4100
|
const wantCi = opts.withCi || autopilot;
|
|
4050
4101
|
if (wantCi) {
|
|
4051
|
-
const ciPath =
|
|
4052
|
-
if (
|
|
4102
|
+
const ciPath = path13.join(root, ".github", "workflows", "haive-sync.yml");
|
|
4103
|
+
if (existsSync12(ciPath)) {
|
|
4053
4104
|
ui.info("CI workflow already exists \u2014 skipped");
|
|
4054
4105
|
} else {
|
|
4055
|
-
await mkdir6(
|
|
4106
|
+
await mkdir6(path13.dirname(ciPath), { recursive: true });
|
|
4056
4107
|
await writeFile7(ciPath, CI_WORKFLOW, "utf8");
|
|
4057
|
-
ui.success(`Created ${
|
|
4108
|
+
ui.success(`Created ${path13.relative(root, ciPath)}`);
|
|
4058
4109
|
}
|
|
4059
4110
|
}
|
|
4060
4111
|
if (autopilot) {
|
|
@@ -4094,7 +4145,7 @@ function registerInit(program2) {
|
|
|
4094
4145
|
interactive: process.stdin.isTTY
|
|
4095
4146
|
});
|
|
4096
4147
|
for (const r of agentSetup.project_results) {
|
|
4097
|
-
if (r.status === "configured" && r.path) ui.success(`hivelore MCP project config written (${
|
|
4148
|
+
if (r.status === "configured" && r.path) ui.success(`hivelore MCP project config written (${path13.relative(root, r.path)})`);
|
|
4098
4149
|
else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
|
|
4099
4150
|
}
|
|
4100
4151
|
for (const r of agentSetup.global_results) {
|
|
@@ -4160,10 +4211,18 @@ function registerInit(program2) {
|
|
|
4160
4211
|
console.log(ui.dim(" Review .ai/project-context.md and fill in the TODO sections."));
|
|
4161
4212
|
console.log(ui.dim(" Or invoke the MCP prompt `bootstrap_project` for a richer AI-generated version."));
|
|
4162
4213
|
}
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4214
|
+
const changelogFile = findDocFile(root, "changelog");
|
|
4215
|
+
const readmeFile = findDocFile(root, "readme");
|
|
4216
|
+
if (changelogFile || readmeFile) {
|
|
4217
|
+
console.log();
|
|
4218
|
+
console.log(ui.dim(" Seed more memories:"));
|
|
4219
|
+
if (changelogFile) {
|
|
4220
|
+
console.log(ui.dim(` hivelore memory import --from ${changelogFile} --changelog \u2014 extract breaking-change gotchas (no AI needed)`));
|
|
4221
|
+
}
|
|
4222
|
+
if (readmeFile) {
|
|
4223
|
+
console.log(ui.dim(` hivelore memory import --from ${readmeFile} \u2014 prepare an import prompt for your AI client`));
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4167
4226
|
} else {
|
|
4168
4227
|
console.log(ui.bold("Next steps:"));
|
|
4169
4228
|
if (!wantBootstrap) {
|
|
@@ -4189,7 +4248,42 @@ async function resolveStacksToSeed(root, stackOpt) {
|
|
|
4189
4248
|
return stackOpt.split(",").map((s) => s.trim().toLowerCase()).filter(isValidStack);
|
|
4190
4249
|
}
|
|
4191
4250
|
async function collectNestedPackageDeps(root) {
|
|
4192
|
-
const SKIP = /* @__PURE__ */ new Set([
|
|
4251
|
+
const SKIP = /* @__PURE__ */ new Set([
|
|
4252
|
+
"node_modules",
|
|
4253
|
+
"dist",
|
|
4254
|
+
"build",
|
|
4255
|
+
"out",
|
|
4256
|
+
".git",
|
|
4257
|
+
".next",
|
|
4258
|
+
"coverage",
|
|
4259
|
+
"vendor",
|
|
4260
|
+
// Fixture/demo trees describe what the project TESTS AGAINST, not what it is built with.
|
|
4261
|
+
// Vite's playground/ pulls react+vue+tailwind+express into detection and seeds four
|
|
4262
|
+
// irrelevant stack packs on a build-tool repo.
|
|
4263
|
+
"playground",
|
|
4264
|
+
"playgrounds",
|
|
4265
|
+
"example",
|
|
4266
|
+
"examples",
|
|
4267
|
+
"fixtures",
|
|
4268
|
+
"__fixtures__",
|
|
4269
|
+
"e2e",
|
|
4270
|
+
"test",
|
|
4271
|
+
"tests",
|
|
4272
|
+
"demo",
|
|
4273
|
+
"demos",
|
|
4274
|
+
"template",
|
|
4275
|
+
"templates",
|
|
4276
|
+
"bench",
|
|
4277
|
+
"benchmarks",
|
|
4278
|
+
"samples",
|
|
4279
|
+
"sandbox",
|
|
4280
|
+
// Doc sites are built with their own framework (VitePress→vue, Docusaurus→react) — that is
|
|
4281
|
+
// the DOCS' stack, not the product's.
|
|
4282
|
+
"docs",
|
|
4283
|
+
"doc",
|
|
4284
|
+
"website"
|
|
4285
|
+
]);
|
|
4286
|
+
const SKIP_PREFIX = /^(template|example|fixture|demo|sample)s?-/;
|
|
4193
4287
|
const merged = {};
|
|
4194
4288
|
let found = false;
|
|
4195
4289
|
async function scan(dir, depth) {
|
|
@@ -4201,10 +4295,10 @@ async function collectNestedPackageDeps(root) {
|
|
|
4201
4295
|
return;
|
|
4202
4296
|
}
|
|
4203
4297
|
for (const entry of entries) {
|
|
4204
|
-
if (!entry.isDirectory() || entry.name.startsWith(".") || SKIP.has(entry.name)) continue;
|
|
4205
|
-
const sub =
|
|
4206
|
-
const pkgPath =
|
|
4207
|
-
if (
|
|
4298
|
+
if (!entry.isDirectory() || entry.name.startsWith(".") || SKIP.has(entry.name) || SKIP_PREFIX.test(entry.name)) continue;
|
|
4299
|
+
const sub = path13.join(dir, entry.name);
|
|
4300
|
+
const pkgPath = path13.join(sub, "package.json");
|
|
4301
|
+
if (existsSync12(pkgPath)) {
|
|
4208
4302
|
try {
|
|
4209
4303
|
const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
|
|
4210
4304
|
Object.assign(merged, pkg.dependencies ?? {}, pkg.devDependencies ?? {});
|
|
@@ -4223,8 +4317,8 @@ async function autoDetectStacksFromRoot(root) {
|
|
|
4223
4317
|
let requirementsTxt;
|
|
4224
4318
|
let goMod;
|
|
4225
4319
|
let pomXml;
|
|
4226
|
-
const pkgPath =
|
|
4227
|
-
if (
|
|
4320
|
+
const pkgPath = path13.join(root, "package.json");
|
|
4321
|
+
if (existsSync12(pkgPath)) {
|
|
4228
4322
|
try {
|
|
4229
4323
|
const pkg = JSON.parse(await readFile6(pkgPath, "utf8"));
|
|
4230
4324
|
packageJsonDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
@@ -4234,8 +4328,8 @@ async function autoDetectStacksFromRoot(root) {
|
|
|
4234
4328
|
const nestedDeps = await collectNestedPackageDeps(root);
|
|
4235
4329
|
if (nestedDeps) packageJsonDeps = { ...packageJsonDeps ?? {}, ...nestedDeps };
|
|
4236
4330
|
for (const name of ["requirements.txt", "requirements/base.txt", "requirements/prod.txt"]) {
|
|
4237
|
-
const reqPath =
|
|
4238
|
-
if (
|
|
4331
|
+
const reqPath = path13.join(root, name);
|
|
4332
|
+
if (existsSync12(reqPath)) {
|
|
4239
4333
|
try {
|
|
4240
4334
|
requirementsTxt = await readFile6(reqPath, "utf8");
|
|
4241
4335
|
break;
|
|
@@ -4243,39 +4337,39 @@ async function autoDetectStacksFromRoot(root) {
|
|
|
4243
4337
|
}
|
|
4244
4338
|
}
|
|
4245
4339
|
}
|
|
4246
|
-
const goModPath =
|
|
4247
|
-
if (
|
|
4340
|
+
const goModPath = path13.join(root, "go.mod");
|
|
4341
|
+
if (existsSync12(goModPath)) {
|
|
4248
4342
|
try {
|
|
4249
4343
|
goMod = await readFile6(goModPath, "utf8");
|
|
4250
4344
|
} catch {
|
|
4251
4345
|
}
|
|
4252
4346
|
}
|
|
4253
|
-
const pomPath =
|
|
4254
|
-
if (
|
|
4347
|
+
const pomPath = path13.join(root, "pom.xml");
|
|
4348
|
+
if (existsSync12(pomPath)) {
|
|
4255
4349
|
try {
|
|
4256
4350
|
pomXml = await readFile6(pomPath, "utf8");
|
|
4257
4351
|
} catch {
|
|
4258
4352
|
}
|
|
4259
4353
|
}
|
|
4260
4354
|
let composerJson;
|
|
4261
|
-
const composerPath =
|
|
4262
|
-
if (
|
|
4355
|
+
const composerPath = path13.join(root, "composer.json");
|
|
4356
|
+
if (existsSync12(composerPath)) {
|
|
4263
4357
|
try {
|
|
4264
4358
|
composerJson = await readFile6(composerPath, "utf8");
|
|
4265
4359
|
} catch {
|
|
4266
4360
|
}
|
|
4267
4361
|
}
|
|
4268
4362
|
let gemfile;
|
|
4269
|
-
const gemfilePath =
|
|
4270
|
-
if (
|
|
4363
|
+
const gemfilePath = path13.join(root, "Gemfile");
|
|
4364
|
+
if (existsSync12(gemfilePath)) {
|
|
4271
4365
|
try {
|
|
4272
4366
|
gemfile = await readFile6(gemfilePath, "utf8");
|
|
4273
4367
|
} catch {
|
|
4274
4368
|
}
|
|
4275
4369
|
}
|
|
4276
|
-
const hasDockerfile =
|
|
4277
|
-
const hasTurboJson =
|
|
4278
|
-
const hasNxJson =
|
|
4370
|
+
const hasDockerfile = existsSync12(path13.join(root, "Dockerfile"));
|
|
4371
|
+
const hasTurboJson = existsSync12(path13.join(root, "turbo.json"));
|
|
4372
|
+
const hasNxJson = existsSync12(path13.join(root, "nx.json"));
|
|
4279
4373
|
let hasCsproj = false;
|
|
4280
4374
|
try {
|
|
4281
4375
|
const entries = await readdir2(root);
|
|
@@ -4327,8 +4421,8 @@ async function seedFromGitHistory(root, paths, limit) {
|
|
|
4327
4421
|
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
4328
4422
|
`;
|
|
4329
4423
|
const file = memoryFilePath2(paths, fm.scope, fm.id, fm.module);
|
|
4330
|
-
if (
|
|
4331
|
-
await mkdir6(
|
|
4424
|
+
if (existsSync12(file)) continue;
|
|
4425
|
+
await mkdir6(path13.dirname(file), { recursive: true });
|
|
4332
4426
|
await writeFile7(file, serializeMemory3({ frontmatter: fm, body }), "utf8");
|
|
4333
4427
|
written++;
|
|
4334
4428
|
}
|
|
@@ -4369,7 +4463,7 @@ function printInitReport(r) {
|
|
|
4369
4463
|
lines.push(` Total ready : ${r.totalMemories} lesson(s), ${r.totalSensors} sensor(s) \u2014 0 written by hand`);
|
|
4370
4464
|
}
|
|
4371
4465
|
if (r.bridgesWritten > 0) {
|
|
4372
|
-
lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (
|
|
4466
|
+
lines.push(` Reach : ${r.bridgesWritten} agent bridge(s) generated (${r.bridgeTargets.join(", ")})`);
|
|
4373
4467
|
}
|
|
4374
4468
|
if (lines.length === 0) return;
|
|
4375
4469
|
const width = Math.max(...lines.map((l) => l.length), 44);
|
|
@@ -4390,33 +4484,37 @@ function printInitReport(r) {
|
|
|
4390
4484
|
}
|
|
4391
4485
|
async function writeCursorHaiveRule(root) {
|
|
4392
4486
|
const relPath = ".cursor/rules/haive-mcp-required.mdc";
|
|
4393
|
-
const target =
|
|
4394
|
-
if (
|
|
4487
|
+
const target = path13.join(root, relPath);
|
|
4488
|
+
if (existsSync12(target)) {
|
|
4395
4489
|
ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
|
|
4396
4490
|
return;
|
|
4397
4491
|
}
|
|
4398
|
-
await mkdir6(
|
|
4492
|
+
await mkdir6(path13.dirname(target), { recursive: true });
|
|
4399
4493
|
await writeFile7(target, CURSOR_HAIVE_RULE_MDC, "utf8");
|
|
4400
4494
|
ui.success(`Created Cursor rule ${relPath}`);
|
|
4401
4495
|
}
|
|
4402
|
-
function resolveBridgeTargets(opt) {
|
|
4403
|
-
const raw = (opt ?? "
|
|
4404
|
-
if (raw === "
|
|
4496
|
+
function resolveBridgeTargets(opt, root) {
|
|
4497
|
+
const raw = (opt ?? "auto").trim().toLowerCase();
|
|
4498
|
+
if (raw === "all") return [...BRIDGE_TARGETS2];
|
|
4499
|
+
if (raw === "" || raw === "auto") {
|
|
4500
|
+
const detection = detectBridgeTargets(root);
|
|
4501
|
+
const named = detection.targets.map((t) => detection.reasons[t] === "repo file" ? `${t} (already in repo)` : t).join(", ");
|
|
4502
|
+
ui.info(`Bridge targets (detected): ${named} \u2014 pass --bridge-targets all for every supported client`);
|
|
4503
|
+
return detection.targets;
|
|
4504
|
+
}
|
|
4405
4505
|
const requested = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
4406
|
-
const valid = requested.filter((t) =>
|
|
4407
|
-
const invalid = requested.filter((t) => !
|
|
4506
|
+
const valid = requested.filter((t) => BRIDGE_TARGETS2.includes(t));
|
|
4507
|
+
const invalid = requested.filter((t) => !BRIDGE_TARGETS2.includes(t));
|
|
4408
4508
|
if (invalid.length > 0) {
|
|
4409
|
-
ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${
|
|
4509
|
+
ui.warn(`Ignoring unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS2.join(", ")}`);
|
|
4410
4510
|
}
|
|
4411
|
-
return valid.length > 0 ? valid : [...
|
|
4511
|
+
return valid.length > 0 ? valid : [...BRIDGE_TARGETS2];
|
|
4412
4512
|
}
|
|
4413
4513
|
var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
|
|
4414
4514
|
|
|
4415
4515
|
Not team truth. Use for machine-local session notes or tooling scratch files.
|
|
4416
4516
|
Official memories belong in .ai/memories/ (versioned in Git).
|
|
4417
4517
|
Only .gitignore and this README are meant to commit; everything else stays untracked.
|
|
4418
|
-
|
|
4419
|
-
Session continuity (local): agents may append \`session-journal.ndjson\` via MCP \`runtime_journal_append\` or \`hivelore runtime journal append\`.
|
|
4420
4518
|
`;
|
|
4421
4519
|
var RUNTIME_GITIGNORE_BODY = `*
|
|
4422
4520
|
!.gitignore
|
|
@@ -4424,27 +4522,27 @@ var RUNTIME_GITIGNORE_BODY = `*
|
|
|
4424
4522
|
`;
|
|
4425
4523
|
async function ensureAiRuntimeLayout(runtimeDir) {
|
|
4426
4524
|
await mkdir6(runtimeDir, { recursive: true });
|
|
4427
|
-
const gi =
|
|
4428
|
-
if (!
|
|
4525
|
+
const gi = path13.join(runtimeDir, ".gitignore");
|
|
4526
|
+
if (!existsSync12(gi)) {
|
|
4429
4527
|
await writeFile7(gi, RUNTIME_GITIGNORE_BODY, "utf8");
|
|
4430
4528
|
}
|
|
4431
|
-
const readme =
|
|
4432
|
-
if (!
|
|
4529
|
+
const readme = path13.join(runtimeDir, "README.md");
|
|
4530
|
+
if (!existsSync12(readme)) {
|
|
4433
4531
|
await writeFile7(readme, RUNTIME_README_BODY, "utf8");
|
|
4434
4532
|
}
|
|
4435
4533
|
}
|
|
4436
4534
|
async function ensureAiCacheLayout(cacheDir) {
|
|
4437
4535
|
await mkdir6(cacheDir, { recursive: true });
|
|
4438
|
-
const gi =
|
|
4439
|
-
if (!
|
|
4536
|
+
const gi = path13.join(cacheDir, ".gitignore");
|
|
4537
|
+
if (!existsSync12(gi)) {
|
|
4440
4538
|
await writeFile7(gi, "*\n!.gitignore\n", "utf8");
|
|
4441
4539
|
}
|
|
4442
4540
|
}
|
|
4443
4541
|
async function ensureGitignoreEntries(root, patterns) {
|
|
4444
4542
|
try {
|
|
4445
|
-
const gitignorePath =
|
|
4543
|
+
const gitignorePath = path13.join(root, ".gitignore");
|
|
4446
4544
|
let existing = "";
|
|
4447
|
-
if (
|
|
4545
|
+
if (existsSync12(gitignorePath)) {
|
|
4448
4546
|
existing = await readFile6(gitignorePath, "utf8");
|
|
4449
4547
|
}
|
|
4450
4548
|
const lines = existing.split("\n");
|
|
@@ -4458,8 +4556,8 @@ async function ensureGitignoreEntries(root, patterns) {
|
|
|
4458
4556
|
|
|
4459
4557
|
// src/commands/observe.ts
|
|
4460
4558
|
import { appendFile, mkdir as mkdir7 } from "fs/promises";
|
|
4461
|
-
import { existsSync as
|
|
4462
|
-
import
|
|
4559
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4560
|
+
import path14 from "path";
|
|
4463
4561
|
import "commander";
|
|
4464
4562
|
import { findProjectRoot as findProjectRoot7, resolveHaivePaths as resolveHaivePaths7 } from "@hivelore/core";
|
|
4465
4563
|
var MAX_STDIN_BYTES = 256 * 1024;
|
|
@@ -4566,7 +4664,7 @@ function registerObserve(program2) {
|
|
|
4566
4664
|
})();
|
|
4567
4665
|
if (!root) return;
|
|
4568
4666
|
const paths = resolveHaivePaths7(root);
|
|
4569
|
-
if (!
|
|
4667
|
+
if (!existsSync13(paths.haiveDir)) return;
|
|
4570
4668
|
const failureHint = detectFailure(payload);
|
|
4571
4669
|
const observation = {
|
|
4572
4670
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4577,10 +4675,10 @@ function registerObserve(program2) {
|
|
|
4577
4675
|
files: extractFiles(payload),
|
|
4578
4676
|
...failureHint ? { failure_hint: true } : {}
|
|
4579
4677
|
};
|
|
4580
|
-
const cacheDir =
|
|
4678
|
+
const cacheDir = path14.join(paths.haiveDir, ".cache");
|
|
4581
4679
|
await mkdir7(cacheDir, { recursive: true });
|
|
4582
4680
|
await appendFile(
|
|
4583
|
-
|
|
4681
|
+
path14.join(cacheDir, "observations.jsonl"),
|
|
4584
4682
|
JSON.stringify(observation) + "\n",
|
|
4585
4683
|
"utf8"
|
|
4586
4684
|
);
|
|
@@ -4611,8 +4709,8 @@ function registerMcp(program2) {
|
|
|
4611
4709
|
// src/commands/sync.ts
|
|
4612
4710
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
4613
4711
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
4614
|
-
import { existsSync as
|
|
4615
|
-
import
|
|
4712
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4713
|
+
import path15 from "path";
|
|
4616
4714
|
import "commander";
|
|
4617
4715
|
import {
|
|
4618
4716
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
@@ -4634,7 +4732,7 @@ import {
|
|
|
4634
4732
|
verifyAnchor,
|
|
4635
4733
|
watchContracts
|
|
4636
4734
|
} from "@hivelore/core";
|
|
4637
|
-
import { BRIDGE_TARGETS as
|
|
4735
|
+
import { BRIDGE_TARGETS as BRIDGE_TARGETS3 } from "@hivelore/core";
|
|
4638
4736
|
var BRIDGE_START = "<!-- haive:memories-start -->";
|
|
4639
4737
|
var BRIDGE_END = "<!-- haive:memories-end -->";
|
|
4640
4738
|
function registerSync(program2) {
|
|
@@ -4649,7 +4747,7 @@ function registerSync(program2) {
|
|
|
4649
4747
|
).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) => {
|
|
4650
4748
|
const root = findProjectRoot9(opts.dir);
|
|
4651
4749
|
const paths = resolveHaivePaths8(root);
|
|
4652
|
-
if (!
|
|
4750
|
+
if (!existsSync14(paths.memoriesDir)) {
|
|
4653
4751
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
4654
4752
|
process.exitCode = 1;
|
|
4655
4753
|
return;
|
|
@@ -4805,7 +4903,7 @@ function registerSync(program2) {
|
|
|
4805
4903
|
if (opts.injectBridge) {
|
|
4806
4904
|
const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
|
|
4807
4905
|
if (opts.bridgeFile) {
|
|
4808
|
-
await injectBridge(
|
|
4906
|
+
await injectBridge(path15.resolve(opts.bridgeFile), paths.memoriesDir, maxInject, root, opts.quiet);
|
|
4809
4907
|
} else if (!dryRun) {
|
|
4810
4908
|
const res = await writeBridgeFiles(root, paths, {
|
|
4811
4909
|
targets: ["claude", "agents"],
|
|
@@ -4834,7 +4932,7 @@ function registerSync(program2) {
|
|
|
4834
4932
|
if (opts.noBridges !== true) {
|
|
4835
4933
|
try {
|
|
4836
4934
|
const res = await writeBridgeFiles(root, paths, {
|
|
4837
|
-
targets:
|
|
4935
|
+
targets: BRIDGE_TARGETS3,
|
|
4838
4936
|
onlyExisting: true,
|
|
4839
4937
|
dryRun
|
|
4840
4938
|
});
|
|
@@ -4954,10 +5052,10 @@ Wait for **explicit confirmation** before acting.
|
|
|
4954
5052
|
topic: `dep-bump-${slugParts}`
|
|
4955
5053
|
});
|
|
4956
5054
|
if (!dryRun) {
|
|
4957
|
-
const teamDir =
|
|
5055
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
4958
5056
|
await mkdir8(teamDir, { recursive: true });
|
|
4959
5057
|
await writeFile8(
|
|
4960
|
-
|
|
5058
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
4961
5059
|
serializeMemory4({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
4962
5060
|
"utf8"
|
|
4963
5061
|
);
|
|
@@ -5023,10 +5121,10 @@ Wait for **explicit confirmation** before acting.
|
|
|
5023
5121
|
topic: `contract-breaking-${diff.contract}`
|
|
5024
5122
|
});
|
|
5025
5123
|
if (!dryRun) {
|
|
5026
|
-
const teamDir =
|
|
5124
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
5027
5125
|
await mkdir8(teamDir, { recursive: true });
|
|
5028
5126
|
await writeFile8(
|
|
5029
|
-
|
|
5127
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
5030
5128
|
serializeMemory4({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
5031
5129
|
"utf8"
|
|
5032
5130
|
);
|
|
@@ -5117,7 +5215,7 @@ function bridgeSummaryLine(body) {
|
|
|
5117
5215
|
return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
|
|
5118
5216
|
}
|
|
5119
5217
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
5120
|
-
if (!
|
|
5218
|
+
if (!existsSync14(memoriesDir)) return;
|
|
5121
5219
|
const all = await loadMemoriesFromDir7(memoriesDir);
|
|
5122
5220
|
const top = all.filter(({ memory: memory2 }) => {
|
|
5123
5221
|
const s = memory2.frontmatter.status;
|
|
@@ -5143,17 +5241,17 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
|
5143
5241
|
` + block + `
|
|
5144
5242
|
|
|
5145
5243
|
${BRIDGE_END}`;
|
|
5146
|
-
const fileExists =
|
|
5244
|
+
const fileExists = existsSync14(bridgeFile);
|
|
5147
5245
|
let existing = fileExists ? await readFile7(bridgeFile, "utf8") : "";
|
|
5148
5246
|
existing = existing.replace(/\r\n/g, "\n");
|
|
5149
5247
|
const startIdx = existing.indexOf(BRIDGE_START);
|
|
5150
5248
|
const endIdx = existing.indexOf(BRIDGE_END);
|
|
5151
5249
|
if (startIdx !== -1 && endIdx === -1) {
|
|
5152
|
-
ui.warn(`${
|
|
5250
|
+
ui.warn(`${path15.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
|
|
5153
5251
|
return;
|
|
5154
5252
|
}
|
|
5155
5253
|
if (startIdx === -1 && endIdx !== -1) {
|
|
5156
|
-
ui.warn(`${
|
|
5254
|
+
ui.warn(`${path15.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
|
|
5157
5255
|
return;
|
|
5158
5256
|
}
|
|
5159
5257
|
let updated;
|
|
@@ -5161,14 +5259,14 @@ ${BRIDGE_END}`;
|
|
|
5161
5259
|
updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
|
|
5162
5260
|
} else {
|
|
5163
5261
|
if (!fileExists && !quiet) {
|
|
5164
|
-
ui.info(`Creating ${
|
|
5262
|
+
ui.info(`Creating ${path15.relative(root, bridgeFile)} with hivelore memory block.`);
|
|
5165
5263
|
}
|
|
5166
5264
|
updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
|
|
5167
5265
|
}
|
|
5168
5266
|
await writeFile8(bridgeFile, updated, "utf8");
|
|
5169
5267
|
if (!quiet) {
|
|
5170
5268
|
console.log(
|
|
5171
|
-
ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${
|
|
5269
|
+
ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path15.relative(root, bridgeFile)}`)
|
|
5172
5270
|
);
|
|
5173
5271
|
}
|
|
5174
5272
|
}
|
|
@@ -5194,8 +5292,8 @@ function collectSinceChanges(root, ref) {
|
|
|
5194
5292
|
// src/commands/memory-add.ts
|
|
5195
5293
|
import { createHash } from "crypto";
|
|
5196
5294
|
import { mkdir as mkdir9, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
|
|
5197
|
-
import { existsSync as
|
|
5198
|
-
import
|
|
5295
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5296
|
+
import path16 from "path";
|
|
5199
5297
|
import { Option } from "commander";
|
|
5200
5298
|
import {
|
|
5201
5299
|
buildFrontmatter as buildFrontmatter4,
|
|
@@ -5237,7 +5335,7 @@ function registerMemoryAdd(memory2) {
|
|
|
5237
5335
|
if (opts.body === void 0 && opts.content !== void 0) opts.body = opts.content;
|
|
5238
5336
|
const root = findProjectRoot10(opts.dir);
|
|
5239
5337
|
const paths = resolveHaivePaths9(root);
|
|
5240
|
-
if (!
|
|
5338
|
+
if (!existsSync15(paths.haiveDir)) {
|
|
5241
5339
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
5242
5340
|
process.exitCode = 1;
|
|
5243
5341
|
return;
|
|
@@ -5254,7 +5352,7 @@ function registerMemoryAdd(memory2) {
|
|
|
5254
5352
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths2(anchorPaths) : [];
|
|
5255
5353
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
5256
5354
|
if (anchorPaths.length > 0) {
|
|
5257
|
-
const missing = anchorPaths.filter((p) => !
|
|
5355
|
+
const missing = anchorPaths.filter((p) => !existsSync15(path16.resolve(root, p)));
|
|
5258
5356
|
if (missing.length > 0) {
|
|
5259
5357
|
ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
|
|
5260
5358
|
for (const p of missing) ui.warn(` \u2717 ${p}`);
|
|
@@ -5267,7 +5365,7 @@ function registerMemoryAdd(memory2) {
|
|
|
5267
5365
|
const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
|
|
5268
5366
|
let body;
|
|
5269
5367
|
if (opts.bodyFile !== void 0) {
|
|
5270
|
-
if (!
|
|
5368
|
+
if (!existsSync15(opts.bodyFile)) {
|
|
5271
5369
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
5272
5370
|
process.exitCode = 1;
|
|
5273
5371
|
return;
|
|
@@ -5283,7 +5381,7 @@ TODO \u2014 write the memory body.
|
|
|
5283
5381
|
`;
|
|
5284
5382
|
}
|
|
5285
5383
|
const scope = opts.scope ?? config.defaultScope ?? "personal";
|
|
5286
|
-
if (
|
|
5384
|
+
if (existsSync15(paths.memoriesDir)) {
|
|
5287
5385
|
const incomingHash = createHash("sha256").update(body.trim()).digest("hex").slice(0, 12);
|
|
5288
5386
|
const allForHash = await loadMemoriesFromDir8(paths.memoriesDir);
|
|
5289
5387
|
const hashDup = allForHash.find(
|
|
@@ -5296,7 +5394,7 @@ TODO \u2014 write the memory body.
|
|
|
5296
5394
|
return;
|
|
5297
5395
|
}
|
|
5298
5396
|
}
|
|
5299
|
-
if (opts.topic &&
|
|
5397
|
+
if (opts.topic && existsSync15(paths.memoriesDir)) {
|
|
5300
5398
|
const existing = await loadMemoriesFromDir8(paths.memoriesDir);
|
|
5301
5399
|
const topicMatch = existing.find(
|
|
5302
5400
|
({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
|
|
@@ -5316,7 +5414,7 @@ TODO \u2014 write the memory body.
|
|
|
5316
5414
|
}
|
|
5317
5415
|
};
|
|
5318
5416
|
await writeFile9(topicMatch.filePath, serializeMemory5({ frontmatter: newFrontmatter, body }), "utf8");
|
|
5319
|
-
ui.success(`Updated (topic upsert) ${
|
|
5417
|
+
ui.success(`Updated (topic upsert) ${path16.relative(root, topicMatch.filePath)}`);
|
|
5320
5418
|
ui.info(`id=${fm.id} revision=${revisionCount}`);
|
|
5321
5419
|
printSensorLoopHint(opts.type, body, newFrontmatter.anchor.paths, Boolean(newFrontmatter.sensor));
|
|
5322
5420
|
await runPostMemoryAutopilot(root, paths, config);
|
|
@@ -5340,13 +5438,13 @@ TODO \u2014 write the memory body.
|
|
|
5340
5438
|
});
|
|
5341
5439
|
if (frontmatter.status === "validated") frontmatter.validated_by = "auto";
|
|
5342
5440
|
const file = memoryFilePath3(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
5343
|
-
await mkdir9(
|
|
5344
|
-
if (
|
|
5441
|
+
await mkdir9(path16.dirname(file), { recursive: true });
|
|
5442
|
+
if (existsSync15(file)) {
|
|
5345
5443
|
ui.error(`Memory already exists at ${file}`);
|
|
5346
5444
|
process.exitCode = 1;
|
|
5347
5445
|
return;
|
|
5348
5446
|
}
|
|
5349
|
-
if (
|
|
5447
|
+
if (existsSync15(paths.memoriesDir)) {
|
|
5350
5448
|
const existing = await loadMemoriesFromDir8(paths.memoriesDir);
|
|
5351
5449
|
const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
|
|
5352
5450
|
const similar = existing.filter(({ memory: memory3 }) => {
|
|
@@ -5359,7 +5457,7 @@ TODO \u2014 write the memory body.
|
|
|
5359
5457
|
}
|
|
5360
5458
|
}
|
|
5361
5459
|
await writeFile9(file, serializeMemory5({ frontmatter, body }), "utf8");
|
|
5362
|
-
ui.success(`Created ${
|
|
5460
|
+
ui.success(`Created ${path16.relative(root, file)}`);
|
|
5363
5461
|
ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
|
|
5364
5462
|
printSensorLoopHint(opts.type, body, anchorPaths, Boolean(frontmatter.sensor));
|
|
5365
5463
|
await runPostMemoryAutopilot(root, paths, config);
|
|
@@ -5453,8 +5551,8 @@ function slugify(value) {
|
|
|
5453
5551
|
}
|
|
5454
5552
|
|
|
5455
5553
|
// src/commands/memory-list.ts
|
|
5456
|
-
import { existsSync as
|
|
5457
|
-
import
|
|
5554
|
+
import { existsSync as existsSync16 } from "fs";
|
|
5555
|
+
import path17 from "path";
|
|
5458
5556
|
import "commander";
|
|
5459
5557
|
import { findProjectRoot as findProjectRoot11, resolveHaivePaths as resolveHaivePaths10 } from "@hivelore/core";
|
|
5460
5558
|
function registerMemoryList(memory2) {
|
|
@@ -5466,7 +5564,7 @@ function registerMemoryList(memory2) {
|
|
|
5466
5564
|
}
|
|
5467
5565
|
const root = findProjectRoot11(opts.dir);
|
|
5468
5566
|
const paths = resolveHaivePaths10(root);
|
|
5469
|
-
if (!
|
|
5567
|
+
if (!existsSync16(paths.memoriesDir)) {
|
|
5470
5568
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
|
|
5471
5569
|
process.exitCode = 1;
|
|
5472
5570
|
return;
|
|
@@ -5505,7 +5603,7 @@ function registerMemoryList(memory2) {
|
|
|
5505
5603
|
);
|
|
5506
5604
|
const title = mem.body.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
5507
5605
|
if (title && title !== fm.id) console.log(` ${title}`);
|
|
5508
|
-
console.log(` ${ui.dim(
|
|
5606
|
+
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
5509
5607
|
}
|
|
5510
5608
|
const totalLabel = clipped > 0 ? `
|
|
5511
5609
|
${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
|
|
@@ -5543,8 +5641,8 @@ function matchesFilters(loaded, opts) {
|
|
|
5543
5641
|
|
|
5544
5642
|
// src/commands/memory-promote.ts
|
|
5545
5643
|
import { mkdir as mkdir10, unlink, writeFile as writeFile10 } from "fs/promises";
|
|
5546
|
-
import { existsSync as
|
|
5547
|
-
import
|
|
5644
|
+
import { existsSync as existsSync17 } from "fs";
|
|
5645
|
+
import path18 from "path";
|
|
5548
5646
|
import "commander";
|
|
5549
5647
|
import {
|
|
5550
5648
|
findProjectRoot as findProjectRoot12,
|
|
@@ -5556,7 +5654,7 @@ function registerMemoryPromote(memory2) {
|
|
|
5556
5654
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
5557
5655
|
const root = findProjectRoot12(opts.dir);
|
|
5558
5656
|
const paths = resolveHaivePaths11(root);
|
|
5559
|
-
if (!
|
|
5657
|
+
if (!existsSync17(paths.memoriesDir)) {
|
|
5560
5658
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
|
|
5561
5659
|
process.exitCode = 1;
|
|
5562
5660
|
return;
|
|
@@ -5591,19 +5689,19 @@ function registerMemoryPromote(memory2) {
|
|
|
5591
5689
|
body: found.memory.body
|
|
5592
5690
|
};
|
|
5593
5691
|
const newPath = memoryFilePath4(paths, "team", updated.frontmatter.id);
|
|
5594
|
-
await mkdir10(
|
|
5692
|
+
await mkdir10(path18.dirname(newPath), { recursive: true });
|
|
5595
5693
|
await writeFile10(newPath, serializeMemory6(updated), "utf8");
|
|
5596
5694
|
await unlink(found.filePath);
|
|
5597
5695
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
5598
|
-
ui.info(`Now at ${
|
|
5696
|
+
ui.info(`Now at ${path18.relative(root, newPath)}`);
|
|
5599
5697
|
console.log(ui.dim(`\u2192 next: hivelore memory approve ${id} (validate for team use)`));
|
|
5600
5698
|
});
|
|
5601
5699
|
}
|
|
5602
5700
|
|
|
5603
5701
|
// src/commands/memory-approve.ts
|
|
5604
|
-
import { existsSync as
|
|
5702
|
+
import { existsSync as existsSync18 } from "fs";
|
|
5605
5703
|
import { writeFile as writeFile11 } from "fs/promises";
|
|
5606
|
-
import
|
|
5704
|
+
import path19 from "path";
|
|
5607
5705
|
import "commander";
|
|
5608
5706
|
import {
|
|
5609
5707
|
findProjectRoot as findProjectRoot13,
|
|
@@ -5614,7 +5712,7 @@ function registerMemoryApprove(memory2) {
|
|
|
5614
5712
|
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) => {
|
|
5615
5713
|
const root = findProjectRoot13(opts.dir);
|
|
5616
5714
|
const paths = resolveHaivePaths12(root);
|
|
5617
|
-
if (!
|
|
5715
|
+
if (!existsSync18(paths.memoriesDir)) {
|
|
5618
5716
|
ui.error(`No .ai/memories at ${root}.`);
|
|
5619
5717
|
process.exitCode = 1;
|
|
5620
5718
|
return;
|
|
@@ -5668,15 +5766,15 @@ function registerMemoryApprove(memory2) {
|
|
|
5668
5766
|
};
|
|
5669
5767
|
await writeFile11(found.filePath, serializeMemory7(next), "utf8");
|
|
5670
5768
|
ui.success(`Approved ${id} (status=validated, by=human)`);
|
|
5671
|
-
ui.info(
|
|
5769
|
+
ui.info(path19.relative(root, found.filePath));
|
|
5672
5770
|
});
|
|
5673
5771
|
}
|
|
5674
5772
|
|
|
5675
5773
|
// src/commands/memory-update.ts
|
|
5676
5774
|
import { spawn } from "child_process";
|
|
5677
5775
|
import { readFile as readFile9, writeFile as writeFile12 } from "fs/promises";
|
|
5678
|
-
import { existsSync as
|
|
5679
|
-
import
|
|
5776
|
+
import { existsSync as existsSync19 } from "fs";
|
|
5777
|
+
import path20 from "path";
|
|
5680
5778
|
import "commander";
|
|
5681
5779
|
import {
|
|
5682
5780
|
findProjectRoot as findProjectRoot14,
|
|
@@ -5689,7 +5787,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
5689
5787
|
const root = findProjectRoot14(opts.dir);
|
|
5690
5788
|
const paths = resolveHaivePaths13(root);
|
|
5691
5789
|
if (opts.edit) {
|
|
5692
|
-
const all =
|
|
5790
|
+
const all = existsSync19(paths.memoriesDir) ? await loadMemoriesFromDir(paths.memoriesDir) : [];
|
|
5693
5791
|
const found = all.find((m) => m.memory.frontmatter.id === id);
|
|
5694
5792
|
if (!found) {
|
|
5695
5793
|
ui.error(`No memory with id "${id}".`);
|
|
@@ -5697,7 +5795,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
5697
5795
|
return;
|
|
5698
5796
|
}
|
|
5699
5797
|
const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
5700
|
-
ui.info(`Opening ${
|
|
5798
|
+
ui.info(`Opening ${path20.relative(root, found.filePath)} with ${editor}\u2026`);
|
|
5701
5799
|
const code = await new Promise((resolve) => {
|
|
5702
5800
|
const child = spawn(editor, [found.filePath], { stdio: "inherit" });
|
|
5703
5801
|
child.on("exit", (c) => resolve(c ?? 0));
|
|
@@ -5714,7 +5812,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
5714
5812
|
}
|
|
5715
5813
|
return;
|
|
5716
5814
|
}
|
|
5717
|
-
if (!
|
|
5815
|
+
if (!existsSync19(paths.memoriesDir)) {
|
|
5718
5816
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
5719
5817
|
process.exitCode = 1;
|
|
5720
5818
|
return;
|
|
@@ -5756,7 +5854,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
5756
5854
|
if (opts.author !== void 0) updated.push("author");
|
|
5757
5855
|
let newBody;
|
|
5758
5856
|
if (opts.bodyFile !== void 0) {
|
|
5759
|
-
if (!
|
|
5857
|
+
if (!existsSync19(opts.bodyFile)) {
|
|
5760
5858
|
ui.error(`--body-file not found: ${opts.bodyFile}`);
|
|
5761
5859
|
process.exitCode = 1;
|
|
5762
5860
|
return;
|
|
@@ -5782,7 +5880,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
5782
5880
|
serializeMemory8({ frontmatter: newFrontmatter, body: newBody }),
|
|
5783
5881
|
"utf8"
|
|
5784
5882
|
);
|
|
5785
|
-
ui.success(`Updated ${
|
|
5883
|
+
ui.success(`Updated ${path20.relative(root, loaded.filePath)}`);
|
|
5786
5884
|
ui.info(`fields: ${updated.join(", ")}`);
|
|
5787
5885
|
});
|
|
5788
5886
|
}
|
|
@@ -5801,8 +5899,8 @@ function parseCsv3(value) {
|
|
|
5801
5899
|
}
|
|
5802
5900
|
|
|
5803
5901
|
// src/commands/memory-tried.ts
|
|
5804
|
-
import { existsSync as
|
|
5805
|
-
import
|
|
5902
|
+
import { existsSync as existsSync20 } from "fs";
|
|
5903
|
+
import path21 from "path";
|
|
5806
5904
|
import "commander";
|
|
5807
5905
|
import {
|
|
5808
5906
|
findProjectRoot as findProjectRoot15,
|
|
@@ -5817,7 +5915,7 @@ function registerMemoryTried(memory2) {
|
|
|
5817
5915
|
).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("--sensor-pattern <regex>", "one-shot: regex matching the FAULTY usage \u2014 validates + attaches a sensor in this call").option("--sensor-command <cmd>", "one-shot BEHAVIOUR sensor: a command (test/script) the gate runs when the diff touches --paths; non-zero exit = lesson fires").option("--sensor-kind <kind>", "with --sensor-command: shell | test (default test)").option("--sensor-timeout <ms>", "with --sensor-command: max runtime in ms (default 120000)").option("--sensor-absent <regex>", "one-shot: regex marking CORRECT usage nearby (excludes it from firing)").option("--sensor-severity <level>", "one-shot sensor severity: warn | block", "block").option("--sensor-message <text>", "one-shot: self-correction message shown when the sensor fires").option("--bad-example <code>", "one-shot: code snippet the sensor must fire on (validation)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
5818
5916
|
const root = findProjectRoot15(opts.dir);
|
|
5819
5917
|
const paths = resolveHaivePaths14(root);
|
|
5820
|
-
if (!
|
|
5918
|
+
if (!existsSync20(paths.haiveDir)) {
|
|
5821
5919
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
5822
5920
|
process.exitCode = 1;
|
|
5823
5921
|
return;
|
|
@@ -5863,7 +5961,7 @@ function registerMemoryTried(memory2) {
|
|
|
5863
5961
|
process.exitCode = 1;
|
|
5864
5962
|
return;
|
|
5865
5963
|
}
|
|
5866
|
-
ui.success(`Recorded: ${
|
|
5964
|
+
ui.success(`Recorded: ${path21.relative(root, result.file_path)}`);
|
|
5867
5965
|
ui.info(`id=${result.id} type=attempt status=validated (auto-approved)`);
|
|
5868
5966
|
if (result.sensor_result) {
|
|
5869
5967
|
if (result.sensor_result.accepted) {
|
|
@@ -5887,8 +5985,8 @@ function registerMemoryTried(memory2) {
|
|
|
5887
5985
|
|
|
5888
5986
|
// src/commands/memory-seed.ts
|
|
5889
5987
|
import { readFile as readFile10 } from "fs/promises";
|
|
5890
|
-
import { existsSync as
|
|
5891
|
-
import
|
|
5988
|
+
import { existsSync as existsSync21 } from "fs";
|
|
5989
|
+
import path22 from "path";
|
|
5892
5990
|
import "commander";
|
|
5893
5991
|
import {
|
|
5894
5992
|
findProjectRoot as findProjectRoot16,
|
|
@@ -5897,7 +5995,7 @@ import {
|
|
|
5897
5995
|
} from "@hivelore/core";
|
|
5898
5996
|
async function readDependencyMap(root) {
|
|
5899
5997
|
try {
|
|
5900
|
-
const raw = await readFile10(
|
|
5998
|
+
const raw = await readFile10(path22.join(root, "package.json"), "utf8");
|
|
5901
5999
|
const pkg = JSON.parse(raw);
|
|
5902
6000
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
5903
6001
|
} catch {
|
|
@@ -5932,7 +6030,7 @@ function registerMemorySeed(memory2) {
|
|
|
5932
6030
|
}
|
|
5933
6031
|
return;
|
|
5934
6032
|
}
|
|
5935
|
-
if (!
|
|
6033
|
+
if (!existsSync21(paths.haiveDir)) {
|
|
5936
6034
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
5937
6035
|
process.exitCode = 1;
|
|
5938
6036
|
return;
|
|
@@ -5988,8 +6086,8 @@ function registerMemorySeed(memory2) {
|
|
|
5988
6086
|
}
|
|
5989
6087
|
|
|
5990
6088
|
// src/commands/memory-query.ts
|
|
5991
|
-
import { existsSync as
|
|
5992
|
-
import
|
|
6089
|
+
import { existsSync as existsSync22 } from "fs";
|
|
6090
|
+
import path23 from "path";
|
|
5993
6091
|
import "commander";
|
|
5994
6092
|
import {
|
|
5995
6093
|
extractSnippet,
|
|
@@ -6005,7 +6103,7 @@ function registerMemoryQuery(memory2) {
|
|
|
6005
6103
|
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) => {
|
|
6006
6104
|
const root = findProjectRoot17(opts.dir);
|
|
6007
6105
|
const paths = resolveHaivePaths16(root);
|
|
6008
|
-
if (!
|
|
6106
|
+
if (!existsSync22(paths.memoriesDir)) {
|
|
6009
6107
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`hivelore init\` first.`);
|
|
6010
6108
|
process.exitCode = 1;
|
|
6011
6109
|
return;
|
|
@@ -6046,7 +6144,7 @@ function registerMemoryQuery(memory2) {
|
|
|
6046
6144
|
const fm = mem.frontmatter;
|
|
6047
6145
|
const statusBadge = ui.statusBadge(fm.status);
|
|
6048
6146
|
console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
|
|
6049
|
-
console.log(` ${ui.dim(
|
|
6147
|
+
console.log(` ${ui.dim(path23.relative(root, filePath))}`);
|
|
6050
6148
|
const snippet = extractSnippet(mem.body, snippetNeedle);
|
|
6051
6149
|
if (snippet) console.log(` ${snippet}`);
|
|
6052
6150
|
}
|
|
@@ -6064,7 +6162,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
6064
6162
|
|
|
6065
6163
|
// src/commands/memory-reject.ts
|
|
6066
6164
|
import { writeFile as writeFile13 } from "fs/promises";
|
|
6067
|
-
import { existsSync as
|
|
6165
|
+
import { existsSync as existsSync23 } from "fs";
|
|
6068
6166
|
import "commander";
|
|
6069
6167
|
import {
|
|
6070
6168
|
findProjectRoot as findProjectRoot18,
|
|
@@ -6078,7 +6176,7 @@ function registerMemoryReject(memory2) {
|
|
|
6078
6176
|
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) => {
|
|
6079
6177
|
const root = findProjectRoot18(opts.dir);
|
|
6080
6178
|
const paths = resolveHaivePaths17(root);
|
|
6081
|
-
if (!
|
|
6179
|
+
if (!existsSync23(paths.memoriesDir)) {
|
|
6082
6180
|
ui.error(`No .ai/memories at ${root}.`);
|
|
6083
6181
|
process.exitCode = 1;
|
|
6084
6182
|
return;
|
|
@@ -6114,9 +6212,9 @@ function registerMemoryReject(memory2) {
|
|
|
6114
6212
|
}
|
|
6115
6213
|
|
|
6116
6214
|
// src/commands/memory-rm.ts
|
|
6117
|
-
import { existsSync as
|
|
6215
|
+
import { existsSync as existsSync24 } from "fs";
|
|
6118
6216
|
import { unlink as unlink2 } from "fs/promises";
|
|
6119
|
-
import
|
|
6217
|
+
import path24 from "path";
|
|
6120
6218
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6121
6219
|
import "commander";
|
|
6122
6220
|
import {
|
|
@@ -6129,7 +6227,7 @@ function registerMemoryRm(memory2) {
|
|
|
6129
6227
|
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) => {
|
|
6130
6228
|
const root = findProjectRoot19(opts.dir);
|
|
6131
6229
|
const paths = resolveHaivePaths18(root);
|
|
6132
|
-
if (!
|
|
6230
|
+
if (!existsSync24(paths.memoriesDir)) {
|
|
6133
6231
|
ui.error(`No .ai/memories at ${root}.`);
|
|
6134
6232
|
process.exitCode = 1;
|
|
6135
6233
|
return;
|
|
@@ -6141,7 +6239,7 @@ function registerMemoryRm(memory2) {
|
|
|
6141
6239
|
process.exitCode = 1;
|
|
6142
6240
|
return;
|
|
6143
6241
|
}
|
|
6144
|
-
const rel =
|
|
6242
|
+
const rel = path24.relative(root, found.filePath);
|
|
6145
6243
|
if (!opts.yes) {
|
|
6146
6244
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
6147
6245
|
const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
|
|
@@ -6165,9 +6263,9 @@ function registerMemoryRm(memory2) {
|
|
|
6165
6263
|
}
|
|
6166
6264
|
|
|
6167
6265
|
// src/commands/memory-show.ts
|
|
6168
|
-
import { existsSync as
|
|
6266
|
+
import { existsSync as existsSync25 } from "fs";
|
|
6169
6267
|
import { readFile as readFile11 } from "fs/promises";
|
|
6170
|
-
import
|
|
6268
|
+
import path25 from "path";
|
|
6171
6269
|
import "commander";
|
|
6172
6270
|
import {
|
|
6173
6271
|
deriveConfidence,
|
|
@@ -6180,7 +6278,7 @@ function registerMemoryShow(memory2) {
|
|
|
6180
6278
|
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) => {
|
|
6181
6279
|
const root = findProjectRoot20(opts.dir);
|
|
6182
6280
|
const paths = resolveHaivePaths19(root);
|
|
6183
|
-
if (!
|
|
6281
|
+
if (!existsSync25(paths.memoriesDir)) {
|
|
6184
6282
|
ui.error(`No .ai/memories at ${root}.`);
|
|
6185
6283
|
process.exitCode = 1;
|
|
6186
6284
|
return;
|
|
@@ -6209,7 +6307,7 @@ function registerMemoryShow(memory2) {
|
|
|
6209
6307
|
if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
|
|
6210
6308
|
if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
|
|
6211
6309
|
console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
|
|
6212
|
-
console.log(`${ui.dim("file:")} ${
|
|
6310
|
+
console.log(`${ui.dim("file:")} ${path25.relative(root, found.filePath)}`);
|
|
6213
6311
|
if (fm.anchor.paths.length || fm.anchor.symbols.length) {
|
|
6214
6312
|
console.log(ui.dim("anchor:"));
|
|
6215
6313
|
if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
|
|
@@ -6224,8 +6322,8 @@ function registerMemoryShow(memory2) {
|
|
|
6224
6322
|
}
|
|
6225
6323
|
|
|
6226
6324
|
// src/commands/memory-stats.ts
|
|
6227
|
-
import { existsSync as
|
|
6228
|
-
import
|
|
6325
|
+
import { existsSync as existsSync26 } from "fs";
|
|
6326
|
+
import path26 from "path";
|
|
6229
6327
|
import "commander";
|
|
6230
6328
|
import {
|
|
6231
6329
|
deriveConfidence as deriveConfidence2,
|
|
@@ -6238,7 +6336,7 @@ function registerMemoryStats(memory2) {
|
|
|
6238
6336
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("--hot", "only unvalidated (draft/proposed) memories read often \u2014 promotion candidates (absorbed `memory hot`)").option("--threshold <n>", "with --hot: minimum read_count to qualify", "3").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
6239
6337
|
const root = findProjectRoot21(opts.dir);
|
|
6240
6338
|
const paths = resolveHaivePaths20(root);
|
|
6241
|
-
if (!
|
|
6339
|
+
if (!existsSync26(paths.memoriesDir)) {
|
|
6242
6340
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
6243
6341
|
process.exitCode = 1;
|
|
6244
6342
|
return;
|
|
@@ -6269,13 +6367,13 @@ function registerMemoryStats(memory2) {
|
|
|
6269
6367
|
console.log(
|
|
6270
6368
|
` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
|
|
6271
6369
|
);
|
|
6272
|
-
console.log(` ${ui.dim(
|
|
6370
|
+
console.log(` ${ui.dim(path26.relative(root, filePath))}`);
|
|
6273
6371
|
}
|
|
6274
6372
|
});
|
|
6275
6373
|
}
|
|
6276
6374
|
|
|
6277
6375
|
// src/commands/memory-impact.ts
|
|
6278
|
-
import { existsSync as
|
|
6376
|
+
import { existsSync as existsSync27 } from "fs";
|
|
6279
6377
|
import "commander";
|
|
6280
6378
|
import {
|
|
6281
6379
|
compareImpact,
|
|
@@ -6292,7 +6390,7 @@ function registerMemoryImpact(memory2) {
|
|
|
6292
6390
|
).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) => {
|
|
6293
6391
|
const root = findProjectRoot22(opts.dir);
|
|
6294
6392
|
const paths = resolveHaivePaths21(root);
|
|
6295
|
-
if (!
|
|
6393
|
+
if (!existsSync27(paths.memoriesDir)) {
|
|
6296
6394
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
6297
6395
|
process.exitCode = 1;
|
|
6298
6396
|
return;
|
|
@@ -6363,7 +6461,7 @@ function pad(value, width) {
|
|
|
6363
6461
|
}
|
|
6364
6462
|
|
|
6365
6463
|
// src/commands/memory-feedback.ts
|
|
6366
|
-
import { existsSync as
|
|
6464
|
+
import { existsSync as existsSync28 } from "fs";
|
|
6367
6465
|
import { writeFile as writeFile14 } from "fs/promises";
|
|
6368
6466
|
import "commander";
|
|
6369
6467
|
import {
|
|
@@ -6390,7 +6488,7 @@ function registerMemoryFeedback(memory2) {
|
|
|
6390
6488
|
}
|
|
6391
6489
|
const root = findProjectRoot23(opts.dir);
|
|
6392
6490
|
const paths = resolveHaivePaths22(root);
|
|
6393
|
-
if (!
|
|
6491
|
+
if (!existsSync28(paths.memoriesDir)) {
|
|
6394
6492
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
6395
6493
|
process.exitCode = 1;
|
|
6396
6494
|
return;
|
|
@@ -6431,8 +6529,8 @@ function registerMemoryFeedback(memory2) {
|
|
|
6431
6529
|
|
|
6432
6530
|
// src/commands/memory-verify.ts
|
|
6433
6531
|
import { writeFile as writeFile15 } from "fs/promises";
|
|
6434
|
-
import { existsSync as
|
|
6435
|
-
import
|
|
6532
|
+
import { existsSync as existsSync29 } from "fs";
|
|
6533
|
+
import path27 from "path";
|
|
6436
6534
|
import "commander";
|
|
6437
6535
|
import {
|
|
6438
6536
|
findProjectRoot as findProjectRoot24,
|
|
@@ -6446,7 +6544,7 @@ function registerMemoryVerify(memory2) {
|
|
|
6446
6544
|
).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) => {
|
|
6447
6545
|
const root = findProjectRoot24(opts.dir);
|
|
6448
6546
|
const paths = resolveHaivePaths23(root);
|
|
6449
|
-
if (!
|
|
6547
|
+
if (!existsSync29(paths.memoriesDir)) {
|
|
6450
6548
|
if (opts.json) {
|
|
6451
6549
|
console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
|
|
6452
6550
|
} else {
|
|
@@ -6474,7 +6572,7 @@ function registerMemoryVerify(memory2) {
|
|
|
6474
6572
|
for (const { memory: mem, filePath } of targets) {
|
|
6475
6573
|
const result = await verifyAnchor2(mem, { projectRoot: root });
|
|
6476
6574
|
const isAnchored = mem.frontmatter.anchor.paths.length > 0 || mem.frontmatter.anchor.symbols.length > 0;
|
|
6477
|
-
const rel =
|
|
6575
|
+
const rel = path27.relative(root, filePath);
|
|
6478
6576
|
if (!isAnchored) {
|
|
6479
6577
|
anchorlessIds.push(mem.frontmatter.id);
|
|
6480
6578
|
entries.push({ id: mem.frontmatter.id, status: "anchorless", path: rel });
|
|
@@ -6567,7 +6665,7 @@ function applyVerification(mem, result) {
|
|
|
6567
6665
|
|
|
6568
6666
|
// src/commands/memory-import.ts
|
|
6569
6667
|
import { readFile as readFile12 } from "fs/promises";
|
|
6570
|
-
import { existsSync as
|
|
6668
|
+
import { existsSync as existsSync30 } from "fs";
|
|
6571
6669
|
import "commander";
|
|
6572
6670
|
import {
|
|
6573
6671
|
findProjectRoot as findProjectRoot25,
|
|
@@ -6584,12 +6682,12 @@ function registerMemoryImport(memory2) {
|
|
|
6584
6682
|
}
|
|
6585
6683
|
const root = findProjectRoot25(opts.dir);
|
|
6586
6684
|
const paths = resolveHaivePaths24(root);
|
|
6587
|
-
if (!
|
|
6685
|
+
if (!existsSync30(paths.haiveDir)) {
|
|
6588
6686
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
6589
6687
|
process.exitCode = 1;
|
|
6590
6688
|
return;
|
|
6591
6689
|
}
|
|
6592
|
-
if (!
|
|
6690
|
+
if (!existsSync30(opts.from)) {
|
|
6593
6691
|
ui.error(`File not found: ${opts.from}`);
|
|
6594
6692
|
process.exitCode = 1;
|
|
6595
6693
|
return;
|
|
@@ -6622,9 +6720,9 @@ function registerMemoryImport(memory2) {
|
|
|
6622
6720
|
}
|
|
6623
6721
|
|
|
6624
6722
|
// src/commands/memory-digest.ts
|
|
6625
|
-
import { existsSync as
|
|
6723
|
+
import { existsSync as existsSync31 } from "fs";
|
|
6626
6724
|
import { writeFile as writeFile16 } from "fs/promises";
|
|
6627
|
-
import
|
|
6725
|
+
import path28 from "path";
|
|
6628
6726
|
import "commander";
|
|
6629
6727
|
import {
|
|
6630
6728
|
deriveConfidence as deriveConfidence3,
|
|
@@ -6647,7 +6745,7 @@ function registerMemoryDigest(program2) {
|
|
|
6647
6745
|
).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) => {
|
|
6648
6746
|
const root = findProjectRoot26(opts.dir);
|
|
6649
6747
|
const paths = resolveHaivePaths25(root);
|
|
6650
|
-
if (!
|
|
6748
|
+
if (!existsSync31(paths.memoriesDir)) {
|
|
6651
6749
|
ui.error("No .ai/memories found. Run `hivelore init` first.");
|
|
6652
6750
|
process.exitCode = 1;
|
|
6653
6751
|
return;
|
|
@@ -6719,7 +6817,7 @@ function registerMemoryDigest(program2) {
|
|
|
6719
6817
|
);
|
|
6720
6818
|
const digest = lines.join("\n");
|
|
6721
6819
|
if (opts.out) {
|
|
6722
|
-
const outPath =
|
|
6820
|
+
const outPath = path28.resolve(process.cwd(), opts.out);
|
|
6723
6821
|
await writeFile16(outPath, digest, "utf8");
|
|
6724
6822
|
ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
|
|
6725
6823
|
} else {
|
|
@@ -6730,9 +6828,9 @@ function registerMemoryDigest(program2) {
|
|
|
6730
6828
|
|
|
6731
6829
|
// src/commands/session-end.ts
|
|
6732
6830
|
import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm } from "fs/promises";
|
|
6733
|
-
import { existsSync as
|
|
6831
|
+
import { existsSync as existsSync32 } from "fs";
|
|
6734
6832
|
import { spawn as spawn2 } from "child_process";
|
|
6735
|
-
import
|
|
6833
|
+
import path29 from "path";
|
|
6736
6834
|
import { Option as Option2 } from "commander";
|
|
6737
6835
|
import {
|
|
6738
6836
|
buildFrontmatter as buildFrontmatter5,
|
|
@@ -6749,8 +6847,8 @@ import {
|
|
|
6749
6847
|
writeSessionHandoff
|
|
6750
6848
|
} from "@hivelore/core";
|
|
6751
6849
|
async function buildAutoRecap(paths) {
|
|
6752
|
-
const obsFile =
|
|
6753
|
-
if (!
|
|
6850
|
+
const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
6851
|
+
if (!existsSync32(obsFile)) return await buildGitAutoRecap(paths);
|
|
6754
6852
|
const raw = await readFile13(obsFile, "utf8").catch(() => "");
|
|
6755
6853
|
if (!raw.trim()) return await buildGitAutoRecap(paths);
|
|
6756
6854
|
const lines = raw.split("\n").filter(Boolean);
|
|
@@ -6918,8 +7016,8 @@ function runGit(cwd, args) {
|
|
|
6918
7016
|
});
|
|
6919
7017
|
}
|
|
6920
7018
|
async function observationStart(paths) {
|
|
6921
|
-
const obsFile =
|
|
6922
|
-
if (!
|
|
7019
|
+
const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
7020
|
+
if (!existsSync32(obsFile)) return null;
|
|
6923
7021
|
const raw = await readFile13(obsFile, "utf8").catch(() => "");
|
|
6924
7022
|
let first = null;
|
|
6925
7023
|
for (const line of raw.split("\n")) {
|
|
@@ -6935,7 +7033,7 @@ async function observationStart(paths) {
|
|
|
6935
7033
|
}
|
|
6936
7034
|
async function printCaughtForYou(paths, since, quiet) {
|
|
6937
7035
|
if (quiet) return;
|
|
6938
|
-
const memories =
|
|
7036
|
+
const memories = existsSync32(paths.memoriesDir) ? await loadMemoriesFromDir10(paths.memoriesDir) : [];
|
|
6939
7037
|
const usage = await loadUsageIndex11(paths);
|
|
6940
7038
|
const events = await loadPreventionEvents(paths);
|
|
6941
7039
|
const summary = summarizeCaughtForYou(events, memories, usage, {
|
|
@@ -6973,7 +7071,7 @@ function registerSessionEnd(session2) {
|
|
|
6973
7071
|
).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").addOption(new Option2("--summary <text>", "alias for --accomplished").hideHelp()).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) => {
|
|
6974
7072
|
const root = findProjectRoot27(opts.dir);
|
|
6975
7073
|
const paths = resolveHaivePaths26(root);
|
|
6976
|
-
if (!
|
|
7074
|
+
if (!existsSync32(paths.haiveDir)) {
|
|
6977
7075
|
if (opts.auto || opts.quiet) return;
|
|
6978
7076
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
6979
7077
|
process.exitCode = 1;
|
|
@@ -7007,15 +7105,15 @@ function registerSessionEnd(session2) {
|
|
|
7007
7105
|
});
|
|
7008
7106
|
const topic = recapTopic(scope, opts.module);
|
|
7009
7107
|
const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
|
|
7010
|
-
const missingPaths = filesTouched.filter((p) => !
|
|
7108
|
+
const missingPaths = filesTouched.filter((p) => !existsSync32(path29.resolve(root, p)));
|
|
7011
7109
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
7012
7110
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
7013
7111
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
7014
7112
|
}
|
|
7015
7113
|
const cleanupObservations = async () => {
|
|
7016
7114
|
if (!opts.auto) return;
|
|
7017
|
-
const obsFile =
|
|
7018
|
-
if (
|
|
7115
|
+
const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
7116
|
+
if (existsSync32(obsFile)) await rm(obsFile).catch(() => {
|
|
7019
7117
|
});
|
|
7020
7118
|
};
|
|
7021
7119
|
const config = await loadConfig6(paths);
|
|
@@ -7039,7 +7137,7 @@ function registerSessionEnd(session2) {
|
|
|
7039
7137
|
}
|
|
7040
7138
|
return;
|
|
7041
7139
|
}
|
|
7042
|
-
if (
|
|
7140
|
+
if (existsSync32(paths.memoriesDir)) {
|
|
7043
7141
|
const existing = await loadMemoriesFromDir10(paths.memoriesDir);
|
|
7044
7142
|
const topicMatch = existing.find(
|
|
7045
7143
|
({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
|
|
@@ -7060,7 +7158,7 @@ function registerSessionEnd(session2) {
|
|
|
7060
7158
|
await cleanupObservations();
|
|
7061
7159
|
if (!opts.quiet) {
|
|
7062
7160
|
ui.success(`Session recap updated (revision #${revisionCount})`);
|
|
7063
|
-
ui.info(`id=${fm.id} file=${
|
|
7161
|
+
ui.info(`id=${fm.id} file=${path29.relative(root, topicMatch.filePath)}`);
|
|
7064
7162
|
await printCaughtForYou(paths, caughtSince, opts.quiet);
|
|
7065
7163
|
ui.info("Tip: `hivelore stats --export-report` generates a usage JSON suitable for dashboards.");
|
|
7066
7164
|
}
|
|
@@ -7078,12 +7176,12 @@ function registerSessionEnd(session2) {
|
|
|
7078
7176
|
status: "validated"
|
|
7079
7177
|
});
|
|
7080
7178
|
const file = memoryFilePath5(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
7081
|
-
await mkdir11(
|
|
7179
|
+
await mkdir11(path29.dirname(file), { recursive: true });
|
|
7082
7180
|
await writeFile17(file, serializeMemory12({ frontmatter, body }), "utf8");
|
|
7083
7181
|
await cleanupObservations();
|
|
7084
7182
|
if (!opts.quiet) {
|
|
7085
7183
|
ui.success(`Session recap created`);
|
|
7086
|
-
ui.info(`id=${frontmatter.id} scope=${scope} file=${
|
|
7184
|
+
ui.info(`id=${frontmatter.id} scope=${scope} file=${path29.relative(root, file)}`);
|
|
7087
7185
|
await printCaughtForYou(paths, caughtSince, opts.quiet);
|
|
7088
7186
|
ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
|
|
7089
7187
|
ui.info("Tip: export a local MCP usage rollup with `hivelore stats --export-report .ai/tool-usage-roi-report.json`.");
|
|
@@ -7096,17 +7194,17 @@ function parseCsv5(value) {
|
|
|
7096
7194
|
}
|
|
7097
7195
|
function normalizeAnchorPath(root, filePath) {
|
|
7098
7196
|
if (!filePath) return filePath;
|
|
7099
|
-
if (!
|
|
7100
|
-
const rel =
|
|
7197
|
+
if (!path29.isAbsolute(filePath)) return filePath;
|
|
7198
|
+
const rel = path29.relative(root, filePath);
|
|
7101
7199
|
if (rel.startsWith("..")) return filePath;
|
|
7102
7200
|
return rel;
|
|
7103
7201
|
}
|
|
7104
7202
|
|
|
7105
7203
|
// src/commands/stats.ts
|
|
7106
7204
|
import "commander";
|
|
7107
|
-
import { existsSync as
|
|
7205
|
+
import { existsSync as existsSync33 } from "fs";
|
|
7108
7206
|
import { mkdir as mkdir12, writeFile as writeFile18 } from "fs/promises";
|
|
7109
|
-
import
|
|
7207
|
+
import path30 from "path";
|
|
7110
7208
|
import {
|
|
7111
7209
|
aggregateUsage,
|
|
7112
7210
|
findProjectRoot as findProjectRoot28,
|
|
@@ -7178,11 +7276,11 @@ function registerStats(program2) {
|
|
|
7178
7276
|
});
|
|
7179
7277
|
}
|
|
7180
7278
|
async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
7181
|
-
const outAbs =
|
|
7279
|
+
const outAbs = path30.isAbsolute(outRelative) ? path30.resolve(outRelative) : path30.resolve(root, outRelative);
|
|
7182
7280
|
const size = await usageLogSize(paths);
|
|
7183
7281
|
let events = await readUsageEvents(paths);
|
|
7184
7282
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
7185
|
-
if (
|
|
7283
|
+
if (existsSync33(paths.memoriesDir)) {
|
|
7186
7284
|
const mems = await loadMemoriesFromDir11(paths.memoriesDir);
|
|
7187
7285
|
for (const { memory: memory2 } of mems) {
|
|
7188
7286
|
const fm = memory2.frontmatter;
|
|
@@ -7212,7 +7310,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
7212
7310
|
ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
|
|
7213
7311
|
events = [];
|
|
7214
7312
|
}
|
|
7215
|
-
await mkdir12(
|
|
7313
|
+
await mkdir12(path30.dirname(outAbs), { recursive: true });
|
|
7216
7314
|
const payload = {
|
|
7217
7315
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7218
7316
|
project_root: root,
|
|
@@ -7401,9 +7499,9 @@ function summarize(name, t0, payload, notes) {
|
|
|
7401
7499
|
}
|
|
7402
7500
|
|
|
7403
7501
|
// src/commands/benchmark.ts
|
|
7404
|
-
import { existsSync as
|
|
7502
|
+
import { existsSync as existsSync34 } from "fs";
|
|
7405
7503
|
import { readdir as readdir3, readFile as readFile14, writeFile as writeFile19 } from "fs/promises";
|
|
7406
|
-
import
|
|
7504
|
+
import path31 from "path";
|
|
7407
7505
|
import "commander";
|
|
7408
7506
|
import { estimateTokens as estimateTokens2, findProjectRoot as findProjectRoot30 } from "@hivelore/core";
|
|
7409
7507
|
function registerBenchmark(program2) {
|
|
@@ -7418,9 +7516,9 @@ function registerBenchmark(program2) {
|
|
|
7418
7516
|
}
|
|
7419
7517
|
const markdown = renderMarkdown(root, summary, rows);
|
|
7420
7518
|
if (opts.out) {
|
|
7421
|
-
const outFile =
|
|
7519
|
+
const outFile = path31.isAbsolute(opts.out) ? opts.out : path31.join(root, opts.out);
|
|
7422
7520
|
await writeFile19(outFile, markdown, "utf8");
|
|
7423
|
-
ui.success(`wrote ${
|
|
7521
|
+
ui.success(`wrote ${path31.relative(process.cwd(), outFile)}`);
|
|
7424
7522
|
return;
|
|
7425
7523
|
}
|
|
7426
7524
|
console.log(markdown);
|
|
@@ -7444,19 +7542,19 @@ function registerBenchmark(program2) {
|
|
|
7444
7542
|
}
|
|
7445
7543
|
function resolveBenchmarkRoot(dir) {
|
|
7446
7544
|
const candidate = dir ?? "benchmarks/agent-benchmark";
|
|
7447
|
-
if (
|
|
7545
|
+
if (path31.isAbsolute(candidate)) return candidate;
|
|
7448
7546
|
const projectRoot = findProjectRoot30(process.cwd());
|
|
7449
|
-
return
|
|
7547
|
+
return path31.join(projectRoot, candidate);
|
|
7450
7548
|
}
|
|
7451
7549
|
async function collectRows(root) {
|
|
7452
|
-
if (!
|
|
7550
|
+
if (!existsSync34(root)) throw new Error(`Benchmark directory not found: ${root}`);
|
|
7453
7551
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
7454
7552
|
const rows = [];
|
|
7455
7553
|
for (const entry of entries) {
|
|
7456
7554
|
if (!entry.isDirectory()) continue;
|
|
7457
|
-
const fixtureDir =
|
|
7458
|
-
const reportFile =
|
|
7459
|
-
if (!
|
|
7555
|
+
const fixtureDir = path31.join(root, entry.name);
|
|
7556
|
+
const reportFile = path31.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
|
|
7557
|
+
if (!existsSync34(reportFile)) continue;
|
|
7460
7558
|
const report = await readFile14(reportFile, "utf8");
|
|
7461
7559
|
rows.push(parseAgentReport(entry.name, report));
|
|
7462
7560
|
}
|
|
@@ -7549,8 +7647,8 @@ function escapeRegExp(value) {
|
|
|
7549
7647
|
|
|
7550
7648
|
// src/commands/eval.ts
|
|
7551
7649
|
import { mkdir as mkdir13, readFile as readFile15, writeFile as writeFile20 } from "fs/promises";
|
|
7552
|
-
import { existsSync as
|
|
7553
|
-
import
|
|
7650
|
+
import { existsSync as existsSync35 } from "fs";
|
|
7651
|
+
import path32 from "path";
|
|
7554
7652
|
import "commander";
|
|
7555
7653
|
import {
|
|
7556
7654
|
aggregateRetrieval,
|
|
@@ -7578,7 +7676,7 @@ function registerEval(program2) {
|
|
|
7578
7676
|
).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) => {
|
|
7579
7677
|
const root = findProjectRoot31(opts.dir);
|
|
7580
7678
|
const paths = resolveHaivePaths29(root);
|
|
7581
|
-
if (!
|
|
7679
|
+
if (!existsSync35(paths.memoriesDir)) {
|
|
7582
7680
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
7583
7681
|
process.exitCode = 1;
|
|
7584
7682
|
return;
|
|
@@ -7650,7 +7748,7 @@ function registerEval(program2) {
|
|
|
7650
7748
|
});
|
|
7651
7749
|
if (!opts.json) ui.success(`Recorded eval score ${report.score}/100 to history.`);
|
|
7652
7750
|
}
|
|
7653
|
-
const baselineFile = opts.baselineFile ?
|
|
7751
|
+
const baselineFile = opts.baselineFile ? path32.isAbsolute(opts.baselineFile) ? opts.baselineFile : path32.join(root, opts.baselineFile) : path32.join(root, ".ai", "eval", "baseline.json");
|
|
7654
7752
|
if (opts.baseline) {
|
|
7655
7753
|
const snapshot = {
|
|
7656
7754
|
saved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7659,18 +7757,18 @@ function registerEval(program2) {
|
|
|
7659
7757
|
report,
|
|
7660
7758
|
gate_precision: gatePrecision
|
|
7661
7759
|
};
|
|
7662
|
-
await mkdir13(
|
|
7760
|
+
await mkdir13(path32.dirname(baselineFile), { recursive: true });
|
|
7663
7761
|
await writeFile20(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
|
|
7664
|
-
if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${
|
|
7762
|
+
if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path32.relative(root, baselineFile)}`);
|
|
7665
7763
|
}
|
|
7666
7764
|
let delta = null;
|
|
7667
7765
|
let gateDelta = null;
|
|
7668
7766
|
if (opts.compare || opts.regressionGate) {
|
|
7669
|
-
if (!
|
|
7767
|
+
if (!existsSync35(baselineFile)) {
|
|
7670
7768
|
if (opts.regressionGate) {
|
|
7671
|
-
if (!opts.json) ui.info(`No baseline at ${
|
|
7769
|
+
if (!opts.json) ui.info(`No baseline at ${path32.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`hivelore eval --baseline\` to enable it.`);
|
|
7672
7770
|
} else {
|
|
7673
|
-
ui.error(`No baseline at ${
|
|
7771
|
+
ui.error(`No baseline at ${path32.relative(root, baselineFile)}. Run \`hivelore eval --baseline\` first.`);
|
|
7674
7772
|
process.exitCode = 1;
|
|
7675
7773
|
return;
|
|
7676
7774
|
}
|
|
@@ -7713,9 +7811,9 @@ function registerEval(program2) {
|
|
|
7713
7811
|
}
|
|
7714
7812
|
const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision, authoredScore);
|
|
7715
7813
|
if (opts.out) {
|
|
7716
|
-
const outFile =
|
|
7814
|
+
const outFile = path32.isAbsolute(opts.out) ? opts.out : path32.join(root, opts.out);
|
|
7717
7815
|
await writeFile20(outFile, md, "utf8");
|
|
7718
|
-
ui.success(`wrote ${
|
|
7816
|
+
ui.success(`wrote ${path32.relative(process.cwd(), outFile)}`);
|
|
7719
7817
|
} else {
|
|
7720
7818
|
console.log(md);
|
|
7721
7819
|
}
|
|
@@ -7799,13 +7897,13 @@ function countCases(spec) {
|
|
|
7799
7897
|
}
|
|
7800
7898
|
async function resolveSpec(opts, root, memoriesDir) {
|
|
7801
7899
|
if (opts.spec) {
|
|
7802
|
-
const file =
|
|
7900
|
+
const file = path32.resolve(opts.spec);
|
|
7803
7901
|
const raw = await readFile15(file, "utf8");
|
|
7804
7902
|
const spec = JSON.parse(raw);
|
|
7805
7903
|
return { spec, source: file, synthesized: 0, authored: countCases(spec) };
|
|
7806
7904
|
}
|
|
7807
|
-
const defaultSpec =
|
|
7808
|
-
if (
|
|
7905
|
+
const defaultSpec = path32.join(root, ".ai", "eval", "spec.json");
|
|
7906
|
+
if (existsSync35(defaultSpec)) {
|
|
7809
7907
|
const raw = await readFile15(defaultSpec, "utf8");
|
|
7810
7908
|
const explicit = JSON.parse(raw);
|
|
7811
7909
|
const memories2 = await loadMemoriesFromDir(memoriesDir);
|
|
@@ -7932,8 +8030,8 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision, authoredScore
|
|
|
7932
8030
|
|
|
7933
8031
|
// src/commands/memory-suggest.ts
|
|
7934
8032
|
import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
|
|
7935
|
-
import { existsSync as
|
|
7936
|
-
import
|
|
8033
|
+
import { existsSync as existsSync36 } from "fs";
|
|
8034
|
+
import path33 from "path";
|
|
7937
8035
|
import "commander";
|
|
7938
8036
|
import {
|
|
7939
8037
|
MemoryTypeSchema,
|
|
@@ -8023,7 +8121,7 @@ function registerMemorySuggest(memory2) {
|
|
|
8023
8121
|
}
|
|
8024
8122
|
const created = [];
|
|
8025
8123
|
const skipped = [];
|
|
8026
|
-
const existing =
|
|
8124
|
+
const existing = existsSync36(paths.memoriesDir) ? await loadMemoriesFromDir12(paths.memoriesDir) : [];
|
|
8027
8125
|
for (const s of top) {
|
|
8028
8126
|
const slug = slugify2(s.query);
|
|
8029
8127
|
if (!slug) {
|
|
@@ -8046,13 +8144,13 @@ function registerMemorySuggest(memory2) {
|
|
|
8046
8144
|
});
|
|
8047
8145
|
const body = renderTemplate(s, fm.id, status);
|
|
8048
8146
|
const file = memoryFilePath6(paths, fm.scope, fm.id, fm.module);
|
|
8049
|
-
await mkdir14(
|
|
8050
|
-
if (
|
|
8051
|
-
skipped.push({ query: s.query, reason: `file already exists at ${
|
|
8147
|
+
await mkdir14(path33.dirname(file), { recursive: true });
|
|
8148
|
+
if (existsSync36(file)) {
|
|
8149
|
+
skipped.push({ query: s.query, reason: `file already exists at ${path33.relative(root, file)}` });
|
|
8052
8150
|
continue;
|
|
8053
8151
|
}
|
|
8054
8152
|
await writeFile21(file, serializeMemory13({ frontmatter: fm, body }), "utf8");
|
|
8055
|
-
created.push({ id: fm.id, file:
|
|
8153
|
+
created.push({ id: fm.id, file: path33.relative(root, file), query: s.query });
|
|
8056
8154
|
}
|
|
8057
8155
|
if (opts.json) {
|
|
8058
8156
|
console.log(JSON.stringify({ created, skipped }, null, 2));
|
|
@@ -8150,8 +8248,8 @@ function truncate2(text, max) {
|
|
|
8150
8248
|
}
|
|
8151
8249
|
|
|
8152
8250
|
// src/commands/memory-conflict-candidates.ts
|
|
8153
|
-
import { existsSync as
|
|
8154
|
-
import
|
|
8251
|
+
import { existsSync as existsSync37 } from "fs";
|
|
8252
|
+
import path34 from "path";
|
|
8155
8253
|
import "commander";
|
|
8156
8254
|
import {
|
|
8157
8255
|
findLexicalConflictPairs,
|
|
@@ -8187,9 +8285,9 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
8187
8285
|
});
|
|
8188
8286
|
}
|
|
8189
8287
|
async function runConflictCandidates(opts) {
|
|
8190
|
-
const root =
|
|
8288
|
+
const root = path34.resolve(opts.dir ?? process.cwd());
|
|
8191
8289
|
const paths = resolveHaivePaths31(findProjectRoot33(root));
|
|
8192
|
-
if (!
|
|
8290
|
+
if (!existsSync37(paths.memoriesDir)) {
|
|
8193
8291
|
ui.error("No memories \u2014 run `hivelore init`.");
|
|
8194
8292
|
process.exitCode = 1;
|
|
8195
8293
|
return;
|
|
@@ -8223,9 +8321,9 @@ async function runConflictCandidates(opts) {
|
|
|
8223
8321
|
}
|
|
8224
8322
|
|
|
8225
8323
|
// src/commands/memory-archive.ts
|
|
8226
|
-
import { existsSync as
|
|
8324
|
+
import { existsSync as existsSync38 } from "fs";
|
|
8227
8325
|
import { writeFile as writeFile22 } from "fs/promises";
|
|
8228
|
-
import
|
|
8326
|
+
import path35 from "path";
|
|
8229
8327
|
import "commander";
|
|
8230
8328
|
import {
|
|
8231
8329
|
findProjectRoot as findProjectRoot34,
|
|
@@ -8244,7 +8342,7 @@ function registerMemoryArchive(memory2) {
|
|
|
8244
8342
|
).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) => {
|
|
8245
8343
|
const root = findProjectRoot34(opts.dir);
|
|
8246
8344
|
const paths = resolveHaivePaths32(root);
|
|
8247
|
-
if (!
|
|
8345
|
+
if (!existsSync38(paths.memoriesDir)) {
|
|
8248
8346
|
ui.error(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
8249
8347
|
process.exitCode = 1;
|
|
8250
8348
|
return;
|
|
@@ -8269,7 +8367,7 @@ function registerMemoryArchive(memory2) {
|
|
|
8269
8367
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
8270
8368
|
const retired = retirementSignal2(fm, mem.body);
|
|
8271
8369
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
8272
|
-
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !
|
|
8370
|
+
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync38(path35.join(paths.root, p)));
|
|
8273
8371
|
const isAnchorless = !hasAnyAnchor;
|
|
8274
8372
|
if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
|
|
8275
8373
|
const u = getUsage8(usage, fm.id);
|
|
@@ -8344,9 +8442,9 @@ function parseDays(input) {
|
|
|
8344
8442
|
}
|
|
8345
8443
|
|
|
8346
8444
|
// src/commands/doctor.ts
|
|
8347
|
-
import { existsSync as
|
|
8445
|
+
import { existsSync as existsSync39, statSync as statSync2 } from "fs";
|
|
8348
8446
|
import { readFile as readFile16, stat, writeFile as writeFile23 } from "fs/promises";
|
|
8349
|
-
import
|
|
8447
|
+
import path36 from "path";
|
|
8350
8448
|
import { execFileSync, execSync } from "child_process";
|
|
8351
8449
|
import "commander";
|
|
8352
8450
|
import {
|
|
@@ -8360,7 +8458,7 @@ import {
|
|
|
8360
8458
|
isStackPackSeed as isStackPackSeed2,
|
|
8361
8459
|
loadCodeMap as loadCodeMap6,
|
|
8362
8460
|
loadConfig as loadConfig10,
|
|
8363
|
-
|
|
8461
|
+
loadMemoriesFromDirDetailed,
|
|
8364
8462
|
loadUsageIndex as loadUsageIndex15,
|
|
8365
8463
|
readUsageEvents as readUsageEvents3,
|
|
8366
8464
|
resolveHaivePaths as resolveHaivePaths33
|
|
@@ -8375,7 +8473,7 @@ function registerDoctor(program2) {
|
|
|
8375
8473
|
const findings = [];
|
|
8376
8474
|
const repairs = [];
|
|
8377
8475
|
const config = await loadConfig10(paths);
|
|
8378
|
-
if (!
|
|
8476
|
+
if (!existsSync39(paths.haiveDir)) {
|
|
8379
8477
|
if (opts.json) {
|
|
8380
8478
|
console.log(JSON.stringify({
|
|
8381
8479
|
initialized: false,
|
|
@@ -8400,7 +8498,7 @@ function registerDoctor(program2) {
|
|
|
8400
8498
|
})
|
|
8401
8499
|
);
|
|
8402
8500
|
}
|
|
8403
|
-
if (!
|
|
8501
|
+
if (!existsSync39(paths.projectContext)) {
|
|
8404
8502
|
findings.push({
|
|
8405
8503
|
severity: "warn",
|
|
8406
8504
|
code: "no-project-context",
|
|
@@ -8420,7 +8518,7 @@ function registerDoctor(program2) {
|
|
|
8420
8518
|
});
|
|
8421
8519
|
} else {
|
|
8422
8520
|
const referenced = extractReferencedPaths(content);
|
|
8423
|
-
const missing = referenced.filter((p) => !
|
|
8521
|
+
const missing = referenced.filter((p) => !existsSync39(path36.resolve(root, p)));
|
|
8424
8522
|
const grounded = referenced.length - missing.length;
|
|
8425
8523
|
if (referenced.length >= 3 && grounded / referenced.length < 0.5) {
|
|
8426
8524
|
findings.push({
|
|
@@ -8442,13 +8540,23 @@ function registerDoctor(program2) {
|
|
|
8442
8540
|
});
|
|
8443
8541
|
}
|
|
8444
8542
|
}
|
|
8445
|
-
const
|
|
8543
|
+
const memoriesDetailed = existsSync39(paths.memoriesDir) ? await loadMemoriesFromDirDetailed(paths.memoriesDir) : { loaded: [], invalid: [] };
|
|
8544
|
+
const memories = memoriesDetailed.loaded;
|
|
8446
8545
|
const now = Date.now();
|
|
8546
|
+
if (memoriesDetailed.invalid.length > 0) {
|
|
8547
|
+
const listed = memoriesDetailed.invalid.slice(0, 5).map((f) => `${path36.relative(root, f.filePath)} (${f.error})`).join("; ");
|
|
8548
|
+
findings.push({
|
|
8549
|
+
severity: "warn",
|
|
8550
|
+
code: "invalid-memory-files",
|
|
8551
|
+
message: `${memoriesDetailed.invalid.length} memory file(s) failed to parse and are INVISIBLE to briefings and the gate: ${listed}`,
|
|
8552
|
+
fix: "Fix the frontmatter (or delete the file) \u2014 a corrupt memory is a silently lost team lesson."
|
|
8553
|
+
});
|
|
8554
|
+
}
|
|
8447
8555
|
if (memories.length === 0) {
|
|
8448
8556
|
findings.push({
|
|
8449
8557
|
severity: "info",
|
|
8450
8558
|
code: "no-memories",
|
|
8451
|
-
message: "No memories yet. Capture knowledge as agents work via mem_save /
|
|
8559
|
+
message: "No memories yet. Capture knowledge as agents work via mem_save / mem_tried."
|
|
8452
8560
|
});
|
|
8453
8561
|
} else {
|
|
8454
8562
|
const usage = await loadUsageIndex15(paths);
|
|
@@ -8458,7 +8566,7 @@ function registerDoctor(program2) {
|
|
|
8458
8566
|
severity: "warn",
|
|
8459
8567
|
code: "stale-memories",
|
|
8460
8568
|
message: `${stale.length} memor${stale.length === 1 ? "y" : "ies"} marked stale (anchored code drifted).`,
|
|
8461
|
-
fix: "hivelore memory verify --update # re-check anchors\
|
|
8569
|
+
fix: "hivelore memory verify --update # re-check anchors\nhivelore memory update <id> # manually refresh body"
|
|
8462
8570
|
});
|
|
8463
8571
|
}
|
|
8464
8572
|
const proposed = memories.filter((m) => m.memory.frontmatter.status === "proposed");
|
|
@@ -8565,8 +8673,8 @@ function registerDoctor(program2) {
|
|
|
8565
8673
|
const anchorPaths = s.paths.length > 0 ? s.paths : m.memory.frontmatter.anchor.paths;
|
|
8566
8674
|
const targets = [];
|
|
8567
8675
|
for (const rel of anchorPaths) {
|
|
8568
|
-
const abs =
|
|
8569
|
-
if (!
|
|
8676
|
+
const abs = path36.resolve(root, rel);
|
|
8677
|
+
if (!existsSync39(abs)) continue;
|
|
8570
8678
|
try {
|
|
8571
8679
|
targets.push({ path: rel, content: await readFile16(abs, "utf8") });
|
|
8572
8680
|
} catch {
|
|
@@ -8655,9 +8763,9 @@ function registerDoctor(program2) {
|
|
|
8655
8763
|
}
|
|
8656
8764
|
}
|
|
8657
8765
|
if (config.enforcement?.requireBriefingFirst) {
|
|
8658
|
-
const claudeSettings =
|
|
8766
|
+
const claudeSettings = path36.join(root, ".claude", "settings.local.json");
|
|
8659
8767
|
let hasClaudeEnforcement = false;
|
|
8660
|
-
if (
|
|
8768
|
+
if (existsSync39(claudeSettings)) {
|
|
8661
8769
|
try {
|
|
8662
8770
|
const { readFile: readFile24 } = await import("fs/promises");
|
|
8663
8771
|
const raw = await readFile24(claudeSettings, "utf8");
|
|
@@ -8683,7 +8791,7 @@ function registerDoctor(program2) {
|
|
|
8683
8791
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
|
|
8684
8792
|
});
|
|
8685
8793
|
}
|
|
8686
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
8794
|
+
findings.push(...await collectInstallFindings(root, "0.34.1"));
|
|
8687
8795
|
findings.push(...await collectToolchainFindings(root));
|
|
8688
8796
|
try {
|
|
8689
8797
|
const legacyRaw = execSync("haive-mcp --version", {
|
|
@@ -8691,7 +8799,7 @@ function registerDoctor(program2) {
|
|
|
8691
8799
|
timeout: 3e3,
|
|
8692
8800
|
stdio: ["ignore", "pipe", "ignore"]
|
|
8693
8801
|
}).trim();
|
|
8694
|
-
const cliVersion = "0.
|
|
8802
|
+
const cliVersion = "0.34.1";
|
|
8695
8803
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
8696
8804
|
findings.push({
|
|
8697
8805
|
severity: "warn",
|
|
@@ -8707,17 +8815,17 @@ npm uninstall -g @hivelore/mcp`
|
|
|
8707
8815
|
}
|
|
8708
8816
|
{
|
|
8709
8817
|
const configPaths = [
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8818
|
+
path36.join(root, ".mcp.json"),
|
|
8819
|
+
path36.join(root, ".cursor", "mcp.json"),
|
|
8820
|
+
path36.join(root, ".vscode", "mcp.json")
|
|
8713
8821
|
];
|
|
8714
8822
|
const staleConfigs = [];
|
|
8715
8823
|
for (const cfgPath of configPaths) {
|
|
8716
|
-
if (!
|
|
8824
|
+
if (!existsSync39(cfgPath)) continue;
|
|
8717
8825
|
try {
|
|
8718
8826
|
const raw = await readFile16(cfgPath, "utf8");
|
|
8719
8827
|
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
8720
|
-
staleConfigs.push(
|
|
8828
|
+
staleConfigs.push(path36.relative(root, cfgPath));
|
|
8721
8829
|
if (opts.fix && !opts.dryRun) {
|
|
8722
8830
|
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "hivelore"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
8723
8831
|
await writeFile23(cfgPath, updated, "utf8");
|
|
@@ -9010,8 +9118,8 @@ which -a hivelore haive`
|
|
|
9010
9118
|
const missingBins = /* @__PURE__ */ new Map();
|
|
9011
9119
|
const staleBins = /* @__PURE__ */ new Map();
|
|
9012
9120
|
for (const rel of integrationFiles) {
|
|
9013
|
-
const file =
|
|
9014
|
-
if (!
|
|
9121
|
+
const file = path36.join(root, rel);
|
|
9122
|
+
if (!existsSync39(file)) continue;
|
|
9015
9123
|
const text = await readFile16(file, "utf8").catch(() => "");
|
|
9016
9124
|
for (const bin of extractAbsoluteHaiveBins(text)) {
|
|
9017
9125
|
const version = versionForBinary(bin);
|
|
@@ -9044,7 +9152,7 @@ which -a hivelore haive`
|
|
|
9044
9152
|
async function collectToolchainFindings(root) {
|
|
9045
9153
|
const findings = [];
|
|
9046
9154
|
const pkg = await readJson(
|
|
9047
|
-
|
|
9155
|
+
path36.join(root, "package.json")
|
|
9048
9156
|
);
|
|
9049
9157
|
const wantsPnpm = pkg?.packageManager?.startsWith("pnpm@") || Object.values(pkg?.scripts ?? {}).some((script) => /\bpnpm\b/.test(script));
|
|
9050
9158
|
if (!wantsPnpm) return findings;
|
|
@@ -9063,10 +9171,10 @@ async function collectToolchainFindings(root) {
|
|
|
9063
9171
|
}
|
|
9064
9172
|
async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
9065
9173
|
const findings = [];
|
|
9066
|
-
const isHaiveWorkspace = ["hivelore-monorepo", "haive-monorepo"].includes((await readJson(
|
|
9174
|
+
const isHaiveWorkspace = ["hivelore-monorepo", "haive-monorepo"].includes((await readJson(path36.join(root, "package.json")))?.name ?? "");
|
|
9067
9175
|
if (!isHaiveWorkspace) return findings;
|
|
9068
|
-
const cliDist =
|
|
9069
|
-
if (!
|
|
9176
|
+
const cliDist = path36.join(root, "packages/cli/dist/index.js");
|
|
9177
|
+
if (!existsSync39(cliDist)) {
|
|
9070
9178
|
findings.push({
|
|
9071
9179
|
severity: "warn",
|
|
9072
9180
|
code: "workspace-dist-missing",
|
|
@@ -9090,7 +9198,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
|
9090
9198
|
"packages/core/src/index.ts",
|
|
9091
9199
|
"packages/mcp/src/server.ts",
|
|
9092
9200
|
"packages/cli/src/index.ts"
|
|
9093
|
-
].map((rel) =>
|
|
9201
|
+
].map((rel) => path36.join(root, rel)).filter(existsSync39);
|
|
9094
9202
|
if (sourceFiles.length > 0) {
|
|
9095
9203
|
const distMtime = statSync2(cliDist).mtimeMs;
|
|
9096
9204
|
const newestSource = Math.max(...sourceFiles.map((file) => statSync2(file).mtimeMs));
|
|
@@ -9108,7 +9216,7 @@ async function collectDistFreshnessFindings(root, expectedVersion) {
|
|
|
9108
9216
|
}
|
|
9109
9217
|
async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
9110
9218
|
const findings = [];
|
|
9111
|
-
const rootPkg = await readJson(
|
|
9219
|
+
const rootPkg = await readJson(path36.join(root, "package.json"));
|
|
9112
9220
|
const workspacePackages = [
|
|
9113
9221
|
"packages/core/package.json",
|
|
9114
9222
|
"packages/embeddings/package.json",
|
|
@@ -9117,7 +9225,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
|
9117
9225
|
];
|
|
9118
9226
|
const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
|
|
9119
9227
|
rel,
|
|
9120
|
-
pkg: await readJson(
|
|
9228
|
+
pkg: await readJson(path36.join(root, rel))
|
|
9121
9229
|
})))).filter((item) => item.pkg);
|
|
9122
9230
|
const isHaiveWorkspace = rootPkg?.name === "hivelore-monorepo" || rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hivelore/"));
|
|
9123
9231
|
if (!isHaiveWorkspace) return findings;
|
|
@@ -9179,7 +9287,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
|
|
|
9179
9287
|
}
|
|
9180
9288
|
}
|
|
9181
9289
|
async function readJson(file) {
|
|
9182
|
-
if (!
|
|
9290
|
+
if (!existsSync39(file)) return null;
|
|
9183
9291
|
try {
|
|
9184
9292
|
return JSON.parse(await readFile16(file, "utf8"));
|
|
9185
9293
|
} catch {
|
|
@@ -9408,23 +9516,23 @@ function runCommand(cmd, args, cwd) {
|
|
|
9408
9516
|
}
|
|
9409
9517
|
|
|
9410
9518
|
// src/commands/resolve-project.ts
|
|
9411
|
-
import
|
|
9519
|
+
import path37 from "path";
|
|
9412
9520
|
import "commander";
|
|
9413
9521
|
import { resolveProjectInfo } from "@hivelore/core";
|
|
9414
9522
|
function registerResolveProject(program2) {
|
|
9415
9523
|
program2.command("resolve-project").description(
|
|
9416
9524
|
"Print JSON for Hivelore project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
|
|
9417
9525
|
).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
|
|
9418
|
-
const info = resolveProjectInfo({ cwd:
|
|
9526
|
+
const info = resolveProjectInfo({ cwd: path37.resolve(opts.dir) });
|
|
9419
9527
|
console.log(JSON.stringify({ ok: true, info }, null, 2));
|
|
9420
9528
|
});
|
|
9421
9529
|
}
|
|
9422
9530
|
|
|
9423
9531
|
// src/commands/enforce.ts
|
|
9424
9532
|
import { execFile as execFile4, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
|
|
9425
|
-
import { existsSync as
|
|
9533
|
+
import { existsSync as existsSync41, statSync as statSync3 } from "fs";
|
|
9426
9534
|
import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as rm2, writeFile as writeFile25 } from "fs/promises";
|
|
9427
|
-
import
|
|
9535
|
+
import path39 from "path";
|
|
9428
9536
|
import { promisify as promisify4 } from "util";
|
|
9429
9537
|
import "commander";
|
|
9430
9538
|
import {
|
|
@@ -9441,7 +9549,7 @@ import {
|
|
|
9441
9549
|
isRetiredMemory as isRetiredMemory2,
|
|
9442
9550
|
loadConfig as loadConfig12,
|
|
9443
9551
|
detectAgentContext,
|
|
9444
|
-
loadMemoriesFromDir as
|
|
9552
|
+
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
9445
9553
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
9446
9554
|
readRecentBriefingMarker,
|
|
9447
9555
|
recordPreventionHits,
|
|
@@ -9457,9 +9565,9 @@ import {
|
|
|
9457
9565
|
} from "@hivelore/core";
|
|
9458
9566
|
|
|
9459
9567
|
// src/utils/claude-hooks.ts
|
|
9460
|
-
import { existsSync as
|
|
9568
|
+
import { existsSync as existsSync40 } from "fs";
|
|
9461
9569
|
import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile24 } from "fs/promises";
|
|
9462
|
-
import
|
|
9570
|
+
import path38 from "path";
|
|
9463
9571
|
var HAIVE_HOOK_TAG = "haive-enforcement";
|
|
9464
9572
|
var POST_TOOL_USE_GROUP = {
|
|
9465
9573
|
matcher: "Edit|Write|Bash",
|
|
@@ -9545,7 +9653,7 @@ function unpatchClaudeSettings(input) {
|
|
|
9545
9653
|
async function installClaudeHooksAtPath(settingsPath) {
|
|
9546
9654
|
let raw = null;
|
|
9547
9655
|
let created = false;
|
|
9548
|
-
if (
|
|
9656
|
+
if (existsSync40(settingsPath)) {
|
|
9549
9657
|
try {
|
|
9550
9658
|
raw = JSON.parse(await readFile17(settingsPath, "utf8"));
|
|
9551
9659
|
} catch {
|
|
@@ -9555,12 +9663,12 @@ async function installClaudeHooksAtPath(settingsPath) {
|
|
|
9555
9663
|
created = true;
|
|
9556
9664
|
}
|
|
9557
9665
|
const patched = patchClaudeSettings(raw);
|
|
9558
|
-
await mkdir15(
|
|
9666
|
+
await mkdir15(path38.dirname(settingsPath), { recursive: true });
|
|
9559
9667
|
await writeFile24(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
|
|
9560
9668
|
return { settingsPath, created };
|
|
9561
9669
|
}
|
|
9562
9670
|
async function uninstallClaudeHooksAtPath(settingsPath) {
|
|
9563
|
-
if (!
|
|
9671
|
+
if (!existsSync40(settingsPath)) {
|
|
9564
9672
|
return { settingsPath, created: false };
|
|
9565
9673
|
}
|
|
9566
9674
|
const raw = JSON.parse(await readFile17(settingsPath, "utf8"));
|
|
@@ -9571,9 +9679,9 @@ async function uninstallClaudeHooksAtPath(settingsPath) {
|
|
|
9571
9679
|
function defaultClaudeSettingsPath(scope, projectRoot) {
|
|
9572
9680
|
if (scope === "user") {
|
|
9573
9681
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
9574
|
-
return
|
|
9682
|
+
return path38.join(home, ".claude", "settings.json");
|
|
9575
9683
|
}
|
|
9576
|
-
return
|
|
9684
|
+
return path38.join(projectRoot, ".claude", "settings.local.json");
|
|
9577
9685
|
}
|
|
9578
9686
|
|
|
9579
9687
|
// src/utils/command-sensors.ts
|
|
@@ -9715,7 +9823,7 @@ function registerEnforce(program2) {
|
|
|
9715
9823
|
ui.success(`Removed Hivelore hooks from ${result.settingsPath}`);
|
|
9716
9824
|
} else {
|
|
9717
9825
|
const result = await installClaudeHooksAtPath(settingsPath);
|
|
9718
|
-
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${
|
|
9826
|
+
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path39.relative(root, result.settingsPath) || result.settingsPath})`);
|
|
9719
9827
|
}
|
|
9720
9828
|
} catch (err) {
|
|
9721
9829
|
ui.warn(`Claude Code hooks not ${opts.removeClaude ? "removed" : "installed"}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -9737,19 +9845,19 @@ function registerEnforce(program2) {
|
|
|
9737
9845
|
enforce.command("cleanup").description("Remove generated Hivelore runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
|
|
9738
9846
|
const root = findProjectRoot37(opts.dir);
|
|
9739
9847
|
const paths = resolveHaivePaths35(root);
|
|
9740
|
-
const cacheDir =
|
|
9741
|
-
if (
|
|
9742
|
-
if (opts.dryRun) ui.info(`would clean ${
|
|
9848
|
+
const cacheDir = path39.join(paths.haiveDir, ".cache");
|
|
9849
|
+
if (existsSync41(cacheDir)) {
|
|
9850
|
+
if (opts.dryRun) ui.info(`would clean ${path39.relative(root, cacheDir)} (preserving .gitignore)`);
|
|
9743
9851
|
else {
|
|
9744
9852
|
const removed = await cleanupCacheDir(cacheDir);
|
|
9745
|
-
ui.success(`cleaned ${
|
|
9853
|
+
ui.success(`cleaned ${path39.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
9746
9854
|
}
|
|
9747
9855
|
}
|
|
9748
|
-
if (
|
|
9749
|
-
if (opts.dryRun) ui.info(`would clean ${
|
|
9856
|
+
if (existsSync41(paths.runtimeDir)) {
|
|
9857
|
+
if (opts.dryRun) ui.info(`would clean ${path39.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
|
|
9750
9858
|
else {
|
|
9751
9859
|
const removed = await cleanupRuntimeDir(paths.runtimeDir);
|
|
9752
|
-
ui.success(`cleaned ${
|
|
9860
|
+
ui.success(`cleaned ${path39.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
9753
9861
|
}
|
|
9754
9862
|
}
|
|
9755
9863
|
});
|
|
@@ -9793,7 +9901,7 @@ function registerEnforce(program2) {
|
|
|
9793
9901
|
const root = resolveRoot(opts.dir, payload);
|
|
9794
9902
|
if (!root) return;
|
|
9795
9903
|
const paths = resolveHaivePaths35(root);
|
|
9796
|
-
if (!
|
|
9904
|
+
if (!existsSync41(paths.haiveDir)) return;
|
|
9797
9905
|
await mkdir16(paths.runtimeDir, { recursive: true });
|
|
9798
9906
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
9799
9907
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this Hivelore-initialized project.";
|
|
@@ -9856,7 +9964,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
9856
9964
|
const root = resolveRoot(opts.dir, payload);
|
|
9857
9965
|
if (!root) return;
|
|
9858
9966
|
const paths = resolveHaivePaths35(root);
|
|
9859
|
-
if (!
|
|
9967
|
+
if (!existsSync41(paths.haiveDir)) return;
|
|
9860
9968
|
if (!isWriteLikeTool(payload)) return;
|
|
9861
9969
|
const config = await loadConfig12(paths);
|
|
9862
9970
|
if (config.enforcement?.requireBriefingFirst === false) return;
|
|
@@ -9921,7 +10029,7 @@ function emitPreToolUseContext(text) {
|
|
|
9921
10029
|
async function buildFinishReport(dir) {
|
|
9922
10030
|
const root = findProjectRoot37(dir);
|
|
9923
10031
|
const paths = resolveHaivePaths35(root);
|
|
9924
|
-
const initialized =
|
|
10032
|
+
const initialized = existsSync41(paths.haiveDir);
|
|
9925
10033
|
const config = initialized ? await loadConfig12(paths) : {};
|
|
9926
10034
|
const mode = config.enforcement?.mode ?? "strict";
|
|
9927
10035
|
const findings = [];
|
|
@@ -10113,8 +10221,8 @@ async function buildFinishReport(dir) {
|
|
|
10113
10221
|
async function checkFailureCapture(paths, config) {
|
|
10114
10222
|
const gate = config.enforcement?.failureCaptureGate ?? "warn";
|
|
10115
10223
|
if (gate === "off") return [];
|
|
10116
|
-
const obsFile =
|
|
10117
|
-
if (!
|
|
10224
|
+
const obsFile = path39.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
10225
|
+
if (!existsSync41(obsFile)) return [];
|
|
10118
10226
|
const failures = [];
|
|
10119
10227
|
try {
|
|
10120
10228
|
const raw = await readFile18(obsFile, "utf8");
|
|
@@ -10131,7 +10239,7 @@ async function checkFailureCapture(paths, config) {
|
|
|
10131
10239
|
return [];
|
|
10132
10240
|
}
|
|
10133
10241
|
if (failures.length === 0) return [];
|
|
10134
|
-
const memories =
|
|
10242
|
+
const memories = existsSync41(paths.memoriesDir) ? await loadMemoriesFromDir14(paths.memoriesDir) : [];
|
|
10135
10243
|
const captureTimes = memories.filter(({ memory: memory2 }) => ["attempt", "gotcha"].includes(memory2.frontmatter.type)).map(({ memory: memory2 }) => memory2.frontmatter.created_at);
|
|
10136
10244
|
const uncaptured = findUncapturedFailures(failures, captureTimes);
|
|
10137
10245
|
if (uncaptured.length === 0) {
|
|
@@ -10166,7 +10274,7 @@ function finishReport(root, initialized, mode, findings, config) {
|
|
|
10166
10274
|
async function runWithEnforcement(command, args, opts) {
|
|
10167
10275
|
const root = findProjectRoot37(opts.dir);
|
|
10168
10276
|
const paths = resolveHaivePaths35(root);
|
|
10169
|
-
if (!
|
|
10277
|
+
if (!existsSync41(paths.haiveDir)) {
|
|
10170
10278
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
10171
10279
|
process.exit(1);
|
|
10172
10280
|
}
|
|
@@ -10185,7 +10293,7 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
10185
10293
|
process.exit(2);
|
|
10186
10294
|
}
|
|
10187
10295
|
ui.info(`Hivelore briefing marker created for wrapped agent session: ${sessionId}`);
|
|
10188
|
-
ui.info(`Briefing written to ${
|
|
10296
|
+
ui.info(`Briefing written to ${path39.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
|
|
10189
10297
|
const child = spawn4(command, args, {
|
|
10190
10298
|
cwd: root,
|
|
10191
10299
|
stdio: "inherit",
|
|
@@ -10237,9 +10345,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
10237
10345
|
source: "haive-run",
|
|
10238
10346
|
memoryIds: briefing.memories.map((m) => m.id)
|
|
10239
10347
|
});
|
|
10240
|
-
const dir =
|
|
10348
|
+
const dir = path39.join(paths.runtimeDir, "enforcement", "briefings");
|
|
10241
10349
|
await mkdir16(dir, { recursive: true });
|
|
10242
|
-
const file =
|
|
10350
|
+
const file = path39.join(dir, `${sessionId}.md`);
|
|
10243
10351
|
const parts = [
|
|
10244
10352
|
"# Hivelore Briefing",
|
|
10245
10353
|
"",
|
|
@@ -10277,7 +10385,7 @@ async function checkBootstrapComplete(paths, config, productionCodeChanged) {
|
|
|
10277
10385
|
projectContextRaw = await readFile18(paths.projectContext, "utf8");
|
|
10278
10386
|
} catch {
|
|
10279
10387
|
}
|
|
10280
|
-
const memories =
|
|
10388
|
+
const memories = existsSync41(paths.memoriesDir) ? await loadMemoriesFromDir14(paths.memoriesDir) : [];
|
|
10281
10389
|
const codeMap = await loadCodeMap7(paths);
|
|
10282
10390
|
const codeFiles = codeMap ? Object.keys(codeMap.files) : [];
|
|
10283
10391
|
let existingModules = [];
|
|
@@ -10309,7 +10417,7 @@ ${renderBootstrapChecklist(assessment)}`,
|
|
|
10309
10417
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
10310
10418
|
const root = findProjectRoot37(dir);
|
|
10311
10419
|
const paths = resolveHaivePaths35(root);
|
|
10312
|
-
const initialized =
|
|
10420
|
+
const initialized = existsSync41(paths.haiveDir);
|
|
10313
10421
|
const config = initialized ? await loadConfig12(paths) : {};
|
|
10314
10422
|
if (initialized) {
|
|
10315
10423
|
await applyLightweightRepairs(root, paths);
|
|
@@ -10343,7 +10451,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
10343
10451
|
findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
|
|
10344
10452
|
});
|
|
10345
10453
|
}
|
|
10346
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
10454
|
+
findings.push(...await inspectIntegrationVersions(root, "0.34.1"));
|
|
10347
10455
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
10348
10456
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
10349
10457
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
|
|
@@ -10446,8 +10554,8 @@ function withCategories(report) {
|
|
|
10446
10554
|
async function hasRecentSessionRecap(paths) {
|
|
10447
10555
|
const handoffAge = await handoffAgeMs(paths.root);
|
|
10448
10556
|
if (handoffAge !== null && handoffAge < SESSION_RECAP_TTL_MS) return true;
|
|
10449
|
-
if (!
|
|
10450
|
-
const all = await
|
|
10557
|
+
if (!existsSync41(paths.memoriesDir)) return false;
|
|
10558
|
+
const all = await loadMemoriesFromDir14(paths.memoriesDir);
|
|
10451
10559
|
return all.some(({ memory: memory2 }) => {
|
|
10452
10560
|
const fm = memory2.frontmatter;
|
|
10453
10561
|
const freshnessDate = fm.verified_at ?? fm.created_at;
|
|
@@ -10455,8 +10563,8 @@ async function hasRecentSessionRecap(paths) {
|
|
|
10455
10563
|
});
|
|
10456
10564
|
}
|
|
10457
10565
|
async function verifyMemoryPolicy(paths, config) {
|
|
10458
|
-
if (!
|
|
10459
|
-
const all = await
|
|
10566
|
+
if (!existsSync41(paths.memoriesDir)) return [];
|
|
10567
|
+
const all = await loadMemoriesFromDir14(paths.memoriesDir);
|
|
10460
10568
|
const findings = [];
|
|
10461
10569
|
const staleImportant = [];
|
|
10462
10570
|
let verified = 0;
|
|
@@ -10493,12 +10601,12 @@ async function verifyMemoryPolicy(paths, config) {
|
|
|
10493
10601
|
return findings;
|
|
10494
10602
|
}
|
|
10495
10603
|
async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
10496
|
-
if (!
|
|
10604
|
+
if (!existsSync41(paths.memoriesDir)) return [];
|
|
10497
10605
|
const changedFiles = (await getChangedFiles(paths.root, stage)).filter((f) => !isGeneratedArtifact(f));
|
|
10498
10606
|
if (changedFiles.length === 0) {
|
|
10499
10607
|
return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
|
|
10500
10608
|
}
|
|
10501
|
-
const all = await
|
|
10609
|
+
const all = await loadMemoriesFromDir14(paths.memoriesDir);
|
|
10502
10610
|
const changedSet = new Set(changedFiles);
|
|
10503
10611
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention"]);
|
|
10504
10612
|
const relevant = all.filter(({ memory: memory2 }) => {
|
|
@@ -10527,7 +10635,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
10527
10635
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
10528
10636
|
const missing = relevant.filter(({ memory: memory2, filePath }) => {
|
|
10529
10637
|
if (consulted.has(memory2.frontmatter.id)) return false;
|
|
10530
|
-
if (changedSet.has(
|
|
10638
|
+
if (changedSet.has(path39.relative(paths.root, filePath))) return false;
|
|
10531
10639
|
return true;
|
|
10532
10640
|
}).map(({ memory: memory2 }) => memory2);
|
|
10533
10641
|
if (missing.length === 0) {
|
|
@@ -10592,7 +10700,7 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
10592
10700
|
(w) => w.level === "review" && !w.reasons.includes("sensor")
|
|
10593
10701
|
);
|
|
10594
10702
|
const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
10595
|
-
const reviewSeenFile =
|
|
10703
|
+
const reviewSeenFile = path39.join(paths.runtimeDir, "enforcement", "review-seen.json");
|
|
10596
10704
|
let reviewSeen = {};
|
|
10597
10705
|
try {
|
|
10598
10706
|
reviewSeen = JSON.parse(await readFile18(reviewSeenFile, "utf8"));
|
|
@@ -10619,7 +10727,7 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
10619
10727
|
for (const [id, at] of Object.entries(reviewSeen)) {
|
|
10620
10728
|
if (!Number.isFinite(Date.parse(at)) || now - Date.parse(at) >= REVIEW_SEEN_TTL_MS) delete reviewSeen[id];
|
|
10621
10729
|
}
|
|
10622
|
-
await mkdir16(
|
|
10730
|
+
await mkdir16(path39.dirname(reviewSeenFile), { recursive: true });
|
|
10623
10731
|
await writeFile25(reviewSeenFile, JSON.stringify(reviewSeen, null, 2), "utf8");
|
|
10624
10732
|
} catch {
|
|
10625
10733
|
}
|
|
@@ -10651,9 +10759,9 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
10651
10759
|
];
|
|
10652
10760
|
}
|
|
10653
10761
|
async function runSensorGate(paths, diff) {
|
|
10654
|
-
if (!diff || !
|
|
10762
|
+
if (!diff || !existsSync41(paths.memoriesDir)) return [];
|
|
10655
10763
|
try {
|
|
10656
|
-
const loaded = await
|
|
10764
|
+
const loaded = await loadMemoriesFromDir14(paths.memoriesDir);
|
|
10657
10765
|
const scannable = loaded.map((l) => l.memory).filter((m) => Boolean(m.frontmatter.sensor) && !isRetiredMemory2(m.frontmatter, m.body));
|
|
10658
10766
|
if (scannable.length === 0) return [];
|
|
10659
10767
|
const targets = sensorTargetsFromDiff(diff).filter((t) => isSensorScannablePath(t.path));
|
|
@@ -10766,16 +10874,16 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
10766
10874
|
for (const entry of entries) {
|
|
10767
10875
|
if (entry.name === ".gitignore" || entry.name === "README.md") continue;
|
|
10768
10876
|
if (entry.name === "enforcement") {
|
|
10769
|
-
removed += await cleanupEnforcementDir(
|
|
10877
|
+
removed += await cleanupEnforcementDir(path39.join(runtimeDir, entry.name));
|
|
10770
10878
|
continue;
|
|
10771
10879
|
}
|
|
10772
|
-
await rm2(
|
|
10880
|
+
await rm2(path39.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
10773
10881
|
removed++;
|
|
10774
10882
|
}
|
|
10775
|
-
await writeFile25(
|
|
10776
|
-
if (!
|
|
10883
|
+
await writeFile25(path39.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
10884
|
+
if (!existsSync41(path39.join(runtimeDir, "README.md"))) {
|
|
10777
10885
|
await writeFile25(
|
|
10778
|
-
|
|
10886
|
+
path39.join(runtimeDir, "README.md"),
|
|
10779
10887
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. Hivelore cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
10780
10888
|
"utf8"
|
|
10781
10889
|
);
|
|
@@ -10788,10 +10896,10 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
10788
10896
|
const entries = await readdir4(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
10789
10897
|
for (const entry of entries) {
|
|
10790
10898
|
if (entry.name === ".gitignore") continue;
|
|
10791
|
-
await rm2(
|
|
10899
|
+
await rm2(path39.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
10792
10900
|
removed++;
|
|
10793
10901
|
}
|
|
10794
|
-
await writeFile25(
|
|
10902
|
+
await writeFile25(path39.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
10795
10903
|
return removed;
|
|
10796
10904
|
}
|
|
10797
10905
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -10799,7 +10907,7 @@ async function cleanupEnforcementDir(enforcementDir) {
|
|
|
10799
10907
|
const entries = await readdir4(enforcementDir, { withFileTypes: true }).catch(() => []);
|
|
10800
10908
|
for (const entry of entries) {
|
|
10801
10909
|
if (entry.name === "briefings") continue;
|
|
10802
|
-
await rm2(
|
|
10910
|
+
await rm2(path39.join(enforcementDir, entry.name), { recursive: true, force: true });
|
|
10803
10911
|
removed++;
|
|
10804
10912
|
}
|
|
10805
10913
|
return removed;
|
|
@@ -10817,8 +10925,8 @@ async function inspectIntegrationVersions(root, expectedVersion) {
|
|
|
10817
10925
|
const missingBins = /* @__PURE__ */ new Map();
|
|
10818
10926
|
const staleBins = /* @__PURE__ */ new Map();
|
|
10819
10927
|
for (const rel of files) {
|
|
10820
|
-
const file =
|
|
10821
|
-
if (!
|
|
10928
|
+
const file = path39.join(root, rel);
|
|
10929
|
+
if (!existsSync41(file)) continue;
|
|
10822
10930
|
const text = await readFile18(file, "utf8").catch(() => "");
|
|
10823
10931
|
for (const bin of extractAbsoluteHaiveBins2(text)) {
|
|
10824
10932
|
const version = versionForBinary2(bin);
|
|
@@ -10935,7 +11043,7 @@ async function resolveCiDiffRange(root) {
|
|
|
10935
11043
|
}
|
|
10936
11044
|
async function resolveGithubEventRange(root) {
|
|
10937
11045
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10938
|
-
if (!eventPath || !
|
|
11046
|
+
if (!eventPath || !existsSync41(eventPath)) return null;
|
|
10939
11047
|
try {
|
|
10940
11048
|
const event = JSON.parse(await readFile18(eventPath, "utf8"));
|
|
10941
11049
|
const prBase = cleanGitSha(event.pull_request?.base?.sha);
|
|
@@ -11040,7 +11148,7 @@ function isShippablePath(file) {
|
|
|
11040
11148
|
}
|
|
11041
11149
|
var CI_SKIP_DIRECTIVE = /\[skip ci\]|\[ci skip\]|\[no ci\]|\[skip actions\]|\*\*\*NO_CI\*\*\*|skip-checks: *true/i;
|
|
11042
11150
|
async function checkCommitMessageSkipCi(root, msgfile) {
|
|
11043
|
-
const file =
|
|
11151
|
+
const file = path39.isAbsolute(msgfile) ? msgfile : path39.join(root, msgfile);
|
|
11044
11152
|
const raw = await readFile18(file, "utf8").catch(() => "");
|
|
11045
11153
|
const cleaned = raw.split("\n").filter((line) => !line.startsWith("#")).join("\n");
|
|
11046
11154
|
if (!CI_SKIP_DIRECTIVE.test(cleaned)) return { block: false, message: "" };
|
|
@@ -11069,7 +11177,7 @@ async function inspectReleaseVersionState(root, upstream) {
|
|
|
11069
11177
|
}
|
|
11070
11178
|
async function readPackageVersion(root, relPath) {
|
|
11071
11179
|
try {
|
|
11072
|
-
const data = JSON.parse(await readFile18(
|
|
11180
|
+
const data = JSON.parse(await readFile18(path39.join(root, relPath), "utf8"));
|
|
11073
11181
|
return typeof data.version === "string" ? data.version : void 0;
|
|
11074
11182
|
} catch {
|
|
11075
11183
|
return void 0;
|
|
@@ -11257,8 +11365,8 @@ function buildScore(findings, threshold = 80) {
|
|
|
11257
11365
|
};
|
|
11258
11366
|
}
|
|
11259
11367
|
async function installGitEnforcement(root) {
|
|
11260
|
-
const hooksDir =
|
|
11261
|
-
if (!
|
|
11368
|
+
const hooksDir = path39.join(root, ".git", "hooks");
|
|
11369
|
+
if (!existsSync41(path39.join(root, ".git"))) {
|
|
11262
11370
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
11263
11371
|
return;
|
|
11264
11372
|
}
|
|
@@ -11314,8 +11422,8 @@ _hivelore sync --quiet --since ORIG_HEAD || true
|
|
|
11314
11422
|
}
|
|
11315
11423
|
];
|
|
11316
11424
|
for (const hook of hooks) {
|
|
11317
|
-
const file =
|
|
11318
|
-
if (
|
|
11425
|
+
const file = path39.join(hooksDir, hook.name);
|
|
11426
|
+
if (existsSync41(file)) {
|
|
11319
11427
|
const current = await readFile18(file, "utf8").catch(() => "");
|
|
11320
11428
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
11321
11429
|
await writeFile25(file, hook.body, "utf8");
|
|
@@ -11332,9 +11440,9 @@ ${hook.body}`, "utf8");
|
|
|
11332
11440
|
ui.success("Installed git hooks: pre-commit, pre-push, commit-msg (blocking) + post-merge, post-rewrite (sync)");
|
|
11333
11441
|
}
|
|
11334
11442
|
async function installCiEnforcement(root) {
|
|
11335
|
-
const workflowPath =
|
|
11336
|
-
await mkdir16(
|
|
11337
|
-
if (
|
|
11443
|
+
const workflowPath = path39.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
11444
|
+
await mkdir16(path39.dirname(workflowPath), { recursive: true });
|
|
11445
|
+
if (existsSync41(workflowPath)) {
|
|
11338
11446
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
11339
11447
|
return;
|
|
11340
11448
|
}
|
|
@@ -11365,7 +11473,7 @@ jobs:
|
|
|
11365
11473
|
HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
11366
11474
|
run: hivelore enforce ci
|
|
11367
11475
|
`, "utf8");
|
|
11368
|
-
ui.success(`Created ${
|
|
11476
|
+
ui.success(`Created ${path39.relative(root, workflowPath)}`);
|
|
11369
11477
|
}
|
|
11370
11478
|
function printReport(report, json, explain = false) {
|
|
11371
11479
|
if (json) {
|
|
@@ -11469,15 +11577,15 @@ function extractToolPaths(payload, root) {
|
|
|
11469
11577
|
}
|
|
11470
11578
|
function normalizeToolPath(file, root) {
|
|
11471
11579
|
const normalized = file.replace(/\\/g, "/");
|
|
11472
|
-
if (!
|
|
11473
|
-
return
|
|
11580
|
+
if (!path39.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
|
|
11581
|
+
return path39.relative(root, normalized).replace(/\\/g, "/");
|
|
11474
11582
|
}
|
|
11475
11583
|
async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
|
|
11476
|
-
if (!
|
|
11584
|
+
if (!existsSync41(paths.memoriesDir)) return [];
|
|
11477
11585
|
const marker = await readRecentBriefingMarker(paths, sessionId);
|
|
11478
11586
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
11479
11587
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
|
|
11480
|
-
const all = await
|
|
11588
|
+
const all = await loadMemoriesFromDir14(paths.memoriesDir);
|
|
11481
11589
|
return all.filter(({ memory: memory2 }) => {
|
|
11482
11590
|
const fm = memory2.frontmatter;
|
|
11483
11591
|
if (!policyTypes.has(fm.type)) return false;
|
|
@@ -11518,7 +11626,7 @@ async function readStdin2(maxBytes) {
|
|
|
11518
11626
|
}
|
|
11519
11627
|
var ATOMIC_STAGE_EXCLUDE = ["/.usage/", "/.runtime/", "/.cache/"];
|
|
11520
11628
|
async function stageResyncedArtifacts(root, paths) {
|
|
11521
|
-
const aiRel =
|
|
11629
|
+
const aiRel = path39.relative(root, paths.haiveDir);
|
|
11522
11630
|
const out = await runCommand2("git", ["diff", "--name-only", "--", aiRel], root).catch(() => "");
|
|
11523
11631
|
const toStage = out.split("\n").map((line) => line.trim()).filter(Boolean).filter((file) => !ATOMIC_STAGE_EXCLUDE.some((excl) => `/${file}`.includes(excl)));
|
|
11524
11632
|
if (toStage.length === 0) return;
|
|
@@ -11545,9 +11653,9 @@ function runCommand2(cmd, args, cwd) {
|
|
|
11545
11653
|
}
|
|
11546
11654
|
|
|
11547
11655
|
// src/commands/release.ts
|
|
11548
|
-
import { existsSync as
|
|
11656
|
+
import { existsSync as existsSync42 } from "fs";
|
|
11549
11657
|
import { readFile as readFile19, writeFile as writeFile26 } from "fs/promises";
|
|
11550
|
-
import
|
|
11658
|
+
import path40 from "path";
|
|
11551
11659
|
import "commander";
|
|
11552
11660
|
import { findProjectRoot as findProjectRoot38 } from "@hivelore/core";
|
|
11553
11661
|
import { execFile as execFile5 } from "child_process";
|
|
@@ -11561,7 +11669,7 @@ var VERSION_FILES2 = [
|
|
|
11561
11669
|
"packages/embeddings/package.json"
|
|
11562
11670
|
];
|
|
11563
11671
|
async function readCurrentVersion(root) {
|
|
11564
|
-
const pkg = JSON.parse(await readFile19(
|
|
11672
|
+
const pkg = JSON.parse(await readFile19(path40.join(root, "package.json"), "utf8"));
|
|
11565
11673
|
if (!pkg.version) throw new Error("Root package.json has no version field.");
|
|
11566
11674
|
return pkg.version;
|
|
11567
11675
|
}
|
|
@@ -11582,8 +11690,8 @@ function registerRelease(program2) {
|
|
|
11582
11690
|
const current = await readCurrentVersion(root);
|
|
11583
11691
|
const next = nextVersion(current, spec);
|
|
11584
11692
|
for (const rel of VERSION_FILES2) {
|
|
11585
|
-
const file =
|
|
11586
|
-
if (!
|
|
11693
|
+
const file = path40.join(root, rel);
|
|
11694
|
+
if (!existsSync42(file)) {
|
|
11587
11695
|
ui.warn(`skip ${rel} (missing)`);
|
|
11588
11696
|
continue;
|
|
11589
11697
|
}
|
|
@@ -11596,8 +11704,8 @@ function registerRelease(program2) {
|
|
|
11596
11704
|
}
|
|
11597
11705
|
await writeFile26(file, updated, "utf8");
|
|
11598
11706
|
}
|
|
11599
|
-
const changelog =
|
|
11600
|
-
if (
|
|
11707
|
+
const changelog = path40.join(root, "CHANGELOG.md");
|
|
11708
|
+
if (existsSync42(changelog)) {
|
|
11601
11709
|
const raw = await readFile19(changelog, "utf8");
|
|
11602
11710
|
const heading = `## [${next}]${opts.title ? ` \u2014 ${opts.title}` : ""}`;
|
|
11603
11711
|
if (!raw.includes(`## [${next}]`)) {
|
|
@@ -11620,8 +11728,8 @@ ${heading}
|
|
|
11620
11728
|
const root = findProjectRoot38(opts.dir);
|
|
11621
11729
|
const version = await readCurrentVersion(root);
|
|
11622
11730
|
for (const rel of VERSION_FILES2.slice(1)) {
|
|
11623
|
-
const file =
|
|
11624
|
-
if (!
|
|
11731
|
+
const file = path40.join(root, rel);
|
|
11732
|
+
if (!existsSync42(file)) continue;
|
|
11625
11733
|
const v = JSON.parse(await readFile19(file, "utf8")).version;
|
|
11626
11734
|
if (v !== version) {
|
|
11627
11735
|
ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
|
|
@@ -11667,9 +11775,9 @@ function registerRun(program2) {
|
|
|
11667
11775
|
|
|
11668
11776
|
// src/commands/sensors.ts
|
|
11669
11777
|
import { execFile as execFile6 } from "child_process";
|
|
11670
|
-
import { existsSync as
|
|
11778
|
+
import { existsSync as existsSync43 } from "fs";
|
|
11671
11779
|
import { chmod as chmod2, mkdir as mkdir17, readFile as readFile20, writeFile as writeFile27 } from "fs/promises";
|
|
11672
|
-
import
|
|
11780
|
+
import path41 from "path";
|
|
11673
11781
|
import { promisify as promisify6 } from "util";
|
|
11674
11782
|
import "commander";
|
|
11675
11783
|
import {
|
|
@@ -11678,7 +11786,7 @@ import {
|
|
|
11678
11786
|
isRetiredMemory as isRetiredMemory3,
|
|
11679
11787
|
judgeProposedSensor,
|
|
11680
11788
|
loadConfig as loadConfig13,
|
|
11681
|
-
loadMemoriesFromDir as
|
|
11789
|
+
loadMemoriesFromDir as loadMemoriesFromDir15,
|
|
11682
11790
|
recordPreventionHits as recordPreventionHits2,
|
|
11683
11791
|
resolveHaivePaths as resolveHaivePaths36,
|
|
11684
11792
|
runSensors as runSensors2,
|
|
@@ -11724,7 +11832,7 @@ function registerSensors(program2) {
|
|
|
11724
11832
|
const root = findProjectRoot39(opts.dir);
|
|
11725
11833
|
const paths = resolveHaivePaths36(root);
|
|
11726
11834
|
const memories = await runnableSensorMemories(paths);
|
|
11727
|
-
const diff = opts.diffFile ? await readFile20(
|
|
11835
|
+
const diff = opts.diffFile ? await readFile20(path41.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
|
|
11728
11836
|
const targets = scannableSensorTargets(diff);
|
|
11729
11837
|
const hits = runSensors2(memories, targets);
|
|
11730
11838
|
const config = await loadConfig13(paths);
|
|
@@ -11812,7 +11920,7 @@ function registerSensors(program2) {
|
|
|
11812
11920
|
}
|
|
11813
11921
|
const root = findProjectRoot39(opts.dir);
|
|
11814
11922
|
const paths = resolveHaivePaths36(root);
|
|
11815
|
-
const loaded =
|
|
11923
|
+
const loaded = existsSync43(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
|
|
11816
11924
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
11817
11925
|
if (!found) {
|
|
11818
11926
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -11877,7 +11985,7 @@ function registerSensors(program2) {
|
|
|
11877
11985
|
return;
|
|
11878
11986
|
}
|
|
11879
11987
|
const root2 = findProjectRoot39(opts.dir);
|
|
11880
|
-
const { proposeSensor } = await import("./server-
|
|
11988
|
+
const { proposeSensor } = await import("./server-FQ5C43MV.js");
|
|
11881
11989
|
const out = await proposeSensor(
|
|
11882
11990
|
{
|
|
11883
11991
|
memory_id: id,
|
|
@@ -11920,7 +12028,7 @@ function registerSensors(program2) {
|
|
|
11920
12028
|
}
|
|
11921
12029
|
const root = findProjectRoot39(opts.dir);
|
|
11922
12030
|
const paths = resolveHaivePaths36(root);
|
|
11923
|
-
const loaded =
|
|
12031
|
+
const loaded = existsSync43(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
|
|
11924
12032
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
11925
12033
|
if (!found) {
|
|
11926
12034
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -11974,13 +12082,13 @@ function registerSensors(program2) {
|
|
|
11974
12082
|
const root = findProjectRoot39(opts.dir);
|
|
11975
12083
|
const paths = resolveHaivePaths36(root);
|
|
11976
12084
|
const rows = await sensorRows(paths);
|
|
11977
|
-
const outDir =
|
|
12085
|
+
const outDir = path41.resolve(root, opts.outDir ?? ".ai/generated");
|
|
11978
12086
|
await mkdir17(outDir, { recursive: true });
|
|
11979
|
-
const outPath =
|
|
12087
|
+
const outPath = path41.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
|
|
11980
12088
|
const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
|
|
11981
12089
|
await writeFile27(outPath, content, "utf8");
|
|
11982
12090
|
if (format === "grep") await chmod2(outPath, 493);
|
|
11983
|
-
ui.success(`Exported ${rows.length} sensor(s): ${
|
|
12091
|
+
ui.success(`Exported ${rows.length} sensor(s): ${path41.relative(root, outPath)}`);
|
|
11984
12092
|
});
|
|
11985
12093
|
}
|
|
11986
12094
|
function deriveProposedMessage(body, pattern, absent) {
|
|
@@ -12013,8 +12121,8 @@ async function sensorRows(paths) {
|
|
|
12013
12121
|
});
|
|
12014
12122
|
}
|
|
12015
12123
|
async function runnableSensorMemories(paths, regexOnly = true) {
|
|
12016
|
-
if (!
|
|
12017
|
-
const loaded = await
|
|
12124
|
+
if (!existsSync43(paths.memoriesDir)) return [];
|
|
12125
|
+
const loaded = await loadMemoriesFromDir15(paths.memoriesDir);
|
|
12018
12126
|
return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
12019
12127
|
const sensor = memory2.frontmatter.sensor;
|
|
12020
12128
|
if (!sensor) return false;
|
|
@@ -12055,15 +12163,15 @@ function shellQuote(value) {
|
|
|
12055
12163
|
}
|
|
12056
12164
|
|
|
12057
12165
|
// src/commands/ingest.ts
|
|
12058
|
-
import { existsSync as
|
|
12166
|
+
import { existsSync as existsSync44 } from "fs";
|
|
12059
12167
|
import { mkdir as mkdir18, readFile as readFile21, writeFile as writeFile28 } from "fs/promises";
|
|
12060
|
-
import
|
|
12168
|
+
import path42 from "path";
|
|
12061
12169
|
import "commander";
|
|
12062
12170
|
import {
|
|
12063
12171
|
draftsFromFindings,
|
|
12064
12172
|
filterNewDrafts,
|
|
12065
12173
|
findProjectRoot as findProjectRoot40,
|
|
12066
|
-
loadMemoriesFromDir as
|
|
12174
|
+
loadMemoriesFromDir as loadMemoriesFromDir16,
|
|
12067
12175
|
memoryFilePath as memoryFilePath7,
|
|
12068
12176
|
parseFindings,
|
|
12069
12177
|
resolveHaivePaths as resolveHaivePaths37,
|
|
@@ -12093,7 +12201,7 @@ function registerIngest(program2) {
|
|
|
12093
12201
|
}
|
|
12094
12202
|
const root = findProjectRoot40(opts.dir);
|
|
12095
12203
|
const paths = resolveHaivePaths37(root);
|
|
12096
|
-
if (!
|
|
12204
|
+
if (!existsSync44(paths.haiveDir)) {
|
|
12097
12205
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
12098
12206
|
process.exitCode = 1;
|
|
12099
12207
|
return;
|
|
@@ -12114,8 +12222,8 @@ function registerIngest(program2) {
|
|
|
12114
12222
|
process.exitCode = 1;
|
|
12115
12223
|
return;
|
|
12116
12224
|
}
|
|
12117
|
-
const reportPath =
|
|
12118
|
-
if (!
|
|
12225
|
+
const reportPath = path42.resolve(root, file);
|
|
12226
|
+
if (!existsSync44(reportPath)) {
|
|
12119
12227
|
ui.error(`Report file not found: ${reportPath}`);
|
|
12120
12228
|
process.exitCode = 1;
|
|
12121
12229
|
return;
|
|
@@ -12147,7 +12255,7 @@ function registerIngest(program2) {
|
|
|
12147
12255
|
process.exitCode = 1;
|
|
12148
12256
|
return;
|
|
12149
12257
|
}
|
|
12150
|
-
const existing =
|
|
12258
|
+
const existing = existsSync44(paths.memoriesDir) ? await loadMemoriesFromDir16(paths.memoriesDir) : [];
|
|
12151
12259
|
const existingTopics = new Set(
|
|
12152
12260
|
existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
|
|
12153
12261
|
);
|
|
@@ -12209,13 +12317,13 @@ function registerIngest(program2) {
|
|
|
12209
12317
|
await writeDraft(paths, draft);
|
|
12210
12318
|
created++;
|
|
12211
12319
|
}
|
|
12212
|
-
ui.success(`Created ${created} proposed memory(ies) under ${
|
|
12320
|
+
ui.success(`Created ${created} proposed memory(ies) under ${path42.relative(root, paths.memoriesDir)}/`);
|
|
12213
12321
|
ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
|
|
12214
12322
|
});
|
|
12215
12323
|
}
|
|
12216
12324
|
async function writeDraft(paths, draft) {
|
|
12217
12325
|
const file = memoryFilePath7(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
|
|
12218
|
-
await mkdir18(
|
|
12326
|
+
await mkdir18(path42.dirname(file), { recursive: true });
|
|
12219
12327
|
await writeFile28(file, serializeMemory16({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
12220
12328
|
return file;
|
|
12221
12329
|
}
|
|
@@ -12257,13 +12365,13 @@ async function fetchSonarIssues(opts) {
|
|
|
12257
12365
|
}
|
|
12258
12366
|
|
|
12259
12367
|
// src/commands/dashboard.ts
|
|
12260
|
-
import { existsSync as
|
|
12368
|
+
import { existsSync as existsSync45 } from "fs";
|
|
12261
12369
|
import "commander";
|
|
12262
12370
|
import {
|
|
12263
12371
|
buildDashboard,
|
|
12264
12372
|
findProjectRoot as findProjectRoot41,
|
|
12265
12373
|
loadConfig as loadConfig14,
|
|
12266
|
-
loadMemoriesFromDir as
|
|
12374
|
+
loadMemoriesFromDir as loadMemoriesFromDir17,
|
|
12267
12375
|
loadPreventionEvents as loadPreventionEvents3,
|
|
12268
12376
|
loadUsageIndex as loadUsageIndex16,
|
|
12269
12377
|
resolveHaivePaths as resolveHaivePaths38
|
|
@@ -12274,12 +12382,12 @@ function registerDashboard(program2) {
|
|
|
12274
12382
|
).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) => {
|
|
12275
12383
|
const root = findProjectRoot41(opts.dir);
|
|
12276
12384
|
const paths = resolveHaivePaths38(root);
|
|
12277
|
-
if (!
|
|
12385
|
+
if (!existsSync45(paths.haiveDir)) {
|
|
12278
12386
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
12279
12387
|
process.exitCode = 1;
|
|
12280
12388
|
return;
|
|
12281
12389
|
}
|
|
12282
|
-
const memories =
|
|
12390
|
+
const memories = existsSync45(paths.memoriesDir) ? await loadMemoriesFromDir17(paths.memoriesDir) : [];
|
|
12283
12391
|
const usage = await loadUsageIndex16(paths);
|
|
12284
12392
|
const preventionEvents = await loadPreventionEvents3(paths);
|
|
12285
12393
|
const config = await loadConfig14(paths);
|
|
@@ -12405,8 +12513,8 @@ function warnNum(n) {
|
|
|
12405
12513
|
// src/commands/dev-link.ts
|
|
12406
12514
|
import { execFile as execFile7 } from "child_process";
|
|
12407
12515
|
import { cp, readFile as readFile22 } from "fs/promises";
|
|
12408
|
-
import { existsSync as
|
|
12409
|
-
import
|
|
12516
|
+
import { existsSync as existsSync46 } from "fs";
|
|
12517
|
+
import path43 from "path";
|
|
12410
12518
|
import { promisify as promisify7 } from "util";
|
|
12411
12519
|
import "commander";
|
|
12412
12520
|
import { findProjectRoot as findProjectRoot42 } from "@hivelore/core";
|
|
@@ -12415,7 +12523,7 @@ function registerDevLink(program2) {
|
|
|
12415
12523
|
const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
|
|
12416
12524
|
dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore (or legacy @hiveai) install so the global binary 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) => {
|
|
12417
12525
|
const root = findProjectRoot42(opts.dir);
|
|
12418
|
-
if (!
|
|
12526
|
+
if (!existsSync46(path43.join(root, "packages", "cli", "dist", "index.js"))) {
|
|
12419
12527
|
ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
|
|
12420
12528
|
process.exitCode = 1;
|
|
12421
12529
|
return;
|
|
@@ -12424,9 +12532,9 @@ function registerDevLink(program2) {
|
|
|
12424
12532
|
try {
|
|
12425
12533
|
globalModules = (await exec5("npm", ["root", "-g"])).stdout.trim();
|
|
12426
12534
|
} catch {
|
|
12427
|
-
globalModules =
|
|
12535
|
+
globalModules = path43.join(path43.dirname(path43.dirname(process.execPath)), "lib", "node_modules");
|
|
12428
12536
|
}
|
|
12429
|
-
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) =>
|
|
12537
|
+
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path43.join(globalModules, scope)).filter((dir) => existsSync46(dir));
|
|
12430
12538
|
if (scopeDirs.length === 0) {
|
|
12431
12539
|
ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
|
|
12432
12540
|
process.exitCode = 1;
|
|
@@ -12434,24 +12542,24 @@ function registerDevLink(program2) {
|
|
|
12434
12542
|
}
|
|
12435
12543
|
const linked = [];
|
|
12436
12544
|
const copyDist = async (fromPkg, toDistDir) => {
|
|
12437
|
-
const from =
|
|
12438
|
-
if (!
|
|
12545
|
+
const from = path43.join(root, "packages", fromPkg, "dist");
|
|
12546
|
+
if (!existsSync46(from) || !existsSync46(path43.dirname(toDistDir))) return;
|
|
12439
12547
|
await cp(from, toDistDir, { recursive: true });
|
|
12440
|
-
linked.push(
|
|
12548
|
+
linked.push(path43.relative(globalModules, toDistDir));
|
|
12441
12549
|
};
|
|
12442
12550
|
for (const globalHive of scopeDirs) {
|
|
12443
|
-
const nestedScope =
|
|
12551
|
+
const nestedScope = path43.basename(globalHive);
|
|
12444
12552
|
for (const pkg of ["cli", "mcp"]) {
|
|
12445
|
-
await copyDist(pkg,
|
|
12553
|
+
await copyDist(pkg, path43.join(globalHive, pkg, "dist"));
|
|
12446
12554
|
for (const nested of ["core", "embeddings"]) {
|
|
12447
|
-
await copyDist(nested,
|
|
12555
|
+
await copyDist(nested, path43.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
|
|
12448
12556
|
}
|
|
12449
12557
|
}
|
|
12450
|
-
await copyDist("core",
|
|
12558
|
+
await copyDist("core", path43.join(globalHive, "core", "dist"));
|
|
12451
12559
|
}
|
|
12452
12560
|
let version = "unknown";
|
|
12453
12561
|
try {
|
|
12454
|
-
version = JSON.parse(await readFile22(
|
|
12562
|
+
version = JSON.parse(await readFile22(path43.join(root, "package.json"), "utf8")).version ?? "unknown";
|
|
12455
12563
|
} catch {
|
|
12456
12564
|
}
|
|
12457
12565
|
if (opts.json) {
|
|
@@ -12462,7 +12570,7 @@ function registerDevLink(program2) {
|
|
|
12462
12570
|
ui.warn("Nothing linked \u2014 no matching dist targets were found in the global install.");
|
|
12463
12571
|
return;
|
|
12464
12572
|
}
|
|
12465
|
-
ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) =>
|
|
12573
|
+
ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path43.basename(d)).join(", ")}`);
|
|
12466
12574
|
for (const t of linked) console.log(` ${ui.dim("\u2192")} ${t}`);
|
|
12467
12575
|
console.log(ui.dim("The global binary now runs your local build (git hooks + MCP included)."));
|
|
12468
12576
|
});
|
|
@@ -12470,8 +12578,8 @@ function registerDevLink(program2) {
|
|
|
12470
12578
|
|
|
12471
12579
|
// src/commands/coverage.ts
|
|
12472
12580
|
import { readFile as readFile23 } from "fs/promises";
|
|
12473
|
-
import { existsSync as
|
|
12474
|
-
import
|
|
12581
|
+
import { existsSync as existsSync47 } from "fs";
|
|
12582
|
+
import path44 from "path";
|
|
12475
12583
|
import "commander";
|
|
12476
12584
|
import {
|
|
12477
12585
|
findCoverageGaps,
|
|
@@ -12481,7 +12589,7 @@ import {
|
|
|
12481
12589
|
tallyHotFiles
|
|
12482
12590
|
} from "@hivelore/core";
|
|
12483
12591
|
async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
12484
|
-
if (!
|
|
12592
|
+
if (!existsSync47(cacheFile)) return [];
|
|
12485
12593
|
const raw = await readFile23(cacheFile, "utf8").catch(() => "");
|
|
12486
12594
|
const files = [];
|
|
12487
12595
|
for (const line of raw.split("\n")) {
|
|
@@ -12495,7 +12603,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
|
12495
12603
|
}
|
|
12496
12604
|
for (const f of obs.files ?? []) {
|
|
12497
12605
|
if (typeof f !== "string" || !f) continue;
|
|
12498
|
-
const rel =
|
|
12606
|
+
const rel = path44.isAbsolute(f) ? path44.relative(root, f) : f;
|
|
12499
12607
|
if (rel.startsWith("..")) continue;
|
|
12500
12608
|
files.push(rel);
|
|
12501
12609
|
}
|
|
@@ -12539,7 +12647,7 @@ function registerCoverage(program2) {
|
|
|
12539
12647
|
}) : null;
|
|
12540
12648
|
const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
|
|
12541
12649
|
const sinceMs = Date.now() - days * 864e5;
|
|
12542
|
-
const agentHotFiles = useAgent ? (await readAgentHotFiles(root,
|
|
12650
|
+
const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path44.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
|
|
12543
12651
|
const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
|
|
12544
12652
|
const memories = await loadMemoriesFromDir(paths.memoriesDir);
|
|
12545
12653
|
const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
|
|
@@ -12577,8 +12685,8 @@ function registerCoverage(program2) {
|
|
|
12577
12685
|
|
|
12578
12686
|
// src/commands/merge-driver.ts
|
|
12579
12687
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
12580
|
-
import { readFileSync, writeFileSync, existsSync as
|
|
12581
|
-
import
|
|
12688
|
+
import { readFileSync, writeFileSync, existsSync as existsSync48 } from "fs";
|
|
12689
|
+
import path45 from "path";
|
|
12582
12690
|
import "commander";
|
|
12583
12691
|
import { findProjectRoot as findProjectRoot44, mergeMemoryVersions } from "@hivelore/core";
|
|
12584
12692
|
var GITATTRIBUTES_MARK = "# Hivelore merge driver";
|
|
@@ -12610,8 +12718,8 @@ function registerMergeDriver(program2) {
|
|
|
12610
12718
|
process.exitCode = 1;
|
|
12611
12719
|
return;
|
|
12612
12720
|
}
|
|
12613
|
-
const gaPath =
|
|
12614
|
-
let content =
|
|
12721
|
+
const gaPath = path45.join(root, ".gitattributes");
|
|
12722
|
+
let content = existsSync48(gaPath) ? readFileSync(gaPath, "utf8") : "";
|
|
12615
12723
|
if (!content.includes(GITATTRIBUTES_MARK)) {
|
|
12616
12724
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
12617
12725
|
content += GITATTRIBUTES_BLOCK + "\n";
|
|
@@ -12625,21 +12733,21 @@ function registerMergeDriver(program2) {
|
|
|
12625
12733
|
}
|
|
12626
12734
|
|
|
12627
12735
|
// src/commands/bridges.ts
|
|
12628
|
-
import { existsSync as
|
|
12629
|
-
import
|
|
12736
|
+
import { existsSync as existsSync49 } from "fs";
|
|
12737
|
+
import path46 from "path";
|
|
12630
12738
|
import "commander";
|
|
12631
12739
|
import {
|
|
12632
12740
|
findProjectRoot as findProjectRoot45,
|
|
12633
12741
|
resolveHaivePaths as resolveHaivePaths40,
|
|
12634
|
-
BRIDGE_TARGET_PATH as
|
|
12635
|
-
BRIDGE_TARGETS as
|
|
12742
|
+
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
|
|
12743
|
+
BRIDGE_TARGETS as BRIDGE_TARGETS4
|
|
12636
12744
|
} from "@hivelore/core";
|
|
12637
12745
|
function registerBridges(program2) {
|
|
12638
12746
|
const bridges = program2.command("bridges").description(
|
|
12639
12747
|
"Generate native agent bridge files from the Hivelore 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 hivelore bridges sync --all\n hivelore bridges sync --only cline,windsurf\n"
|
|
12640
12748
|
);
|
|
12641
12749
|
bridges.command("sync").description(
|
|
12642
|
-
"Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " +
|
|
12750
|
+
"Regenerate bridge files idempotently (marker-based, preserves manual content outside markers).\n Supported targets: " + BRIDGE_TARGETS4.join(", ") + "\n"
|
|
12643
12751
|
).option("--all", "generate all supported bridge targets").option(
|
|
12644
12752
|
"--only <targets>",
|
|
12645
12753
|
"comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
|
|
@@ -12647,7 +12755,7 @@ function registerBridges(program2) {
|
|
|
12647
12755
|
const root = findProjectRoot45(opts.dir);
|
|
12648
12756
|
const paths = resolveHaivePaths40(root);
|
|
12649
12757
|
const dryRun = opts.dryRun === true;
|
|
12650
|
-
if (!
|
|
12758
|
+
if (!existsSync49(paths.memoriesDir)) {
|
|
12651
12759
|
ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
12652
12760
|
process.exitCode = 1;
|
|
12653
12761
|
return;
|
|
@@ -12655,18 +12763,18 @@ function registerBridges(program2) {
|
|
|
12655
12763
|
let targets;
|
|
12656
12764
|
if (opts.only) {
|
|
12657
12765
|
const requested = opts.only.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
|
|
12658
|
-
const invalid = requested.filter((t) => !
|
|
12766
|
+
const invalid = requested.filter((t) => !BRIDGE_TARGETS4.includes(t));
|
|
12659
12767
|
if (invalid.length > 0) {
|
|
12660
|
-
ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${
|
|
12768
|
+
ui.error(`Unknown bridge target(s): ${invalid.join(", ")}. Valid: ${BRIDGE_TARGETS4.join(", ")}`);
|
|
12661
12769
|
process.exitCode = 1;
|
|
12662
12770
|
return;
|
|
12663
12771
|
}
|
|
12664
12772
|
targets = requested;
|
|
12665
12773
|
} else if (opts.all) {
|
|
12666
|
-
targets =
|
|
12774
|
+
targets = BRIDGE_TARGETS4;
|
|
12667
12775
|
} else {
|
|
12668
|
-
targets =
|
|
12669
|
-
(t) =>
|
|
12776
|
+
targets = BRIDGE_TARGETS4.filter(
|
|
12777
|
+
(t) => existsSync49(path46.join(root, BRIDGE_TARGET_PATH3[t]))
|
|
12670
12778
|
);
|
|
12671
12779
|
if (targets.length === 0) {
|
|
12672
12780
|
ui.info(
|
|
@@ -12698,7 +12806,7 @@ function registerBridges(program2) {
|
|
|
12698
12806
|
const root = findProjectRoot45(opts.dir);
|
|
12699
12807
|
const paths = resolveHaivePaths40(root);
|
|
12700
12808
|
const statuses = await getBridgeFileStatuses(root, paths, {
|
|
12701
|
-
targets:
|
|
12809
|
+
targets: BRIDGE_TARGETS4,
|
|
12702
12810
|
maxMemories: Math.max(1, Number(opts.maxMemories ?? 8))
|
|
12703
12811
|
});
|
|
12704
12812
|
console.log(ui.bold("Hivelore bridge targets:"));
|
|
@@ -12714,7 +12822,7 @@ function registerBridges(program2) {
|
|
|
12714
12822
|
|
|
12715
12823
|
// src/index.ts
|
|
12716
12824
|
var program = new Command48();
|
|
12717
|
-
program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.
|
|
12825
|
+
program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.34.1").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
|
|
12718
12826
|
registerInit(program);
|
|
12719
12827
|
registerResolveProject(program);
|
|
12720
12828
|
registerEnforce(program);
|