@hiveai/core 0.13.9 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -1
- package/dist/index.js +163 -128
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -856,6 +856,37 @@ function computeRecurrence(events) {
|
|
|
856
856
|
return { recurring_count: rows.length, top: rows };
|
|
857
857
|
}
|
|
858
858
|
|
|
859
|
+
// src/context-throttle.ts
|
|
860
|
+
import { createHash } from "crypto";
|
|
861
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
|
|
862
|
+
import { existsSync as existsSync5 } from "fs";
|
|
863
|
+
import path7 from "path";
|
|
864
|
+
var PROJECT_CONTEXT_THROTTLE_MS = 8 * 60 * 1e3;
|
|
865
|
+
function throttleMarkerPath(paths) {
|
|
866
|
+
return path7.join(paths.haiveDir, ".cache", "briefing-context.json");
|
|
867
|
+
}
|
|
868
|
+
function hashProjectContext(content) {
|
|
869
|
+
return createHash("sha1").update(content).digest("hex").slice(0, 16);
|
|
870
|
+
}
|
|
871
|
+
async function projectContextRecentlyEmitted(paths, hash, now = Date.now()) {
|
|
872
|
+
const file = throttleMarkerPath(paths);
|
|
873
|
+
if (!existsSync5(file)) return false;
|
|
874
|
+
try {
|
|
875
|
+
const m = JSON.parse(await readFile5(file, "utf8"));
|
|
876
|
+
if (m.hash !== hash || !m.at) return false;
|
|
877
|
+
return now - Date.parse(m.at) < PROJECT_CONTEXT_THROTTLE_MS;
|
|
878
|
+
} catch {
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function recordProjectContextEmission(paths, hash, now = Date.now()) {
|
|
883
|
+
const file = throttleMarkerPath(paths);
|
|
884
|
+
await mkdir3(path7.dirname(file), { recursive: true }).catch(() => {
|
|
885
|
+
});
|
|
886
|
+
await writeFile2(file, JSON.stringify({ hash, at: new Date(now).toISOString() }), "utf8").catch(() => {
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
859
890
|
// src/eval.ts
|
|
860
891
|
function round32(n) {
|
|
861
892
|
return Math.round(n * 1e3) / 1e3;
|
|
@@ -1291,10 +1322,10 @@ function allocateBudget(parts, maxTokens) {
|
|
|
1291
1322
|
}
|
|
1292
1323
|
|
|
1293
1324
|
// src/code-map.ts
|
|
1294
|
-
import { mkdir as
|
|
1295
|
-
import { existsSync as
|
|
1325
|
+
import { mkdir as mkdir4, readFile as readFile6, readdir as readdir3, writeFile as writeFile3 } from "fs/promises";
|
|
1326
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1296
1327
|
import { spawnSync } from "child_process";
|
|
1297
|
-
import
|
|
1328
|
+
import path8 from "path";
|
|
1298
1329
|
var CODE_MAP_FILE = "code-map.json";
|
|
1299
1330
|
var DEFAULT_INCLUDE = [
|
|
1300
1331
|
".ts",
|
|
@@ -1336,27 +1367,27 @@ var DEFAULT_EXCLUDE = [
|
|
|
1336
1367
|
];
|
|
1337
1368
|
var TEST_FILE_RE = /\.(test|spec)\.[a-z]+$/i;
|
|
1338
1369
|
function codeMapPath(paths) {
|
|
1339
|
-
return
|
|
1370
|
+
return path8.join(paths.haiveDir, CODE_MAP_FILE);
|
|
1340
1371
|
}
|
|
1341
1372
|
async function loadCodeMap(paths) {
|
|
1342
1373
|
const file = codeMapPath(paths);
|
|
1343
|
-
if (!
|
|
1344
|
-
return JSON.parse(await
|
|
1374
|
+
if (!existsSync6(file)) return null;
|
|
1375
|
+
return JSON.parse(await readFile6(file, "utf8"));
|
|
1345
1376
|
}
|
|
1346
1377
|
async function saveCodeMap(paths, map) {
|
|
1347
1378
|
const file = codeMapPath(paths);
|
|
1348
|
-
await
|
|
1349
|
-
await
|
|
1379
|
+
await mkdir4(path8.dirname(file), { recursive: true });
|
|
1380
|
+
await writeFile3(file, JSON.stringify(map, null, 2), "utf8");
|
|
1350
1381
|
}
|
|
1351
1382
|
async function buildCodeMap(root, options = {}) {
|
|
1352
1383
|
const include = new Set(options.includeExtensions ?? DEFAULT_INCLUDE);
|
|
1353
1384
|
const exclude = new Set(options.excludeDirs ?? DEFAULT_EXCLUDE);
|
|
1354
1385
|
const files = {};
|
|
1355
1386
|
for await (const abs of collectSourceFiles(root, include, exclude, options.includeUntracked)) {
|
|
1356
|
-
const rel =
|
|
1387
|
+
const rel = path8.relative(root, abs).replace(/\\/g, "/");
|
|
1357
1388
|
if (rel.startsWith(".ai/")) continue;
|
|
1358
|
-
const content = await
|
|
1359
|
-
const ext =
|
|
1389
|
+
const content = await readFile6(abs, "utf8");
|
|
1390
|
+
const ext = path8.extname(abs).toLowerCase();
|
|
1360
1391
|
const entry = parseFile(content, ext);
|
|
1361
1392
|
if (entry.exports.length > 0) files[rel] = entry;
|
|
1362
1393
|
}
|
|
@@ -1370,7 +1401,7 @@ async function buildCodeMap(root, options = {}) {
|
|
|
1370
1401
|
async function* collectSourceFiles(root, include, exclude, includeUntracked) {
|
|
1371
1402
|
const gitFiles = gitSourceFiles(root, include, exclude, includeUntracked === true);
|
|
1372
1403
|
if (gitFiles) {
|
|
1373
|
-
for (const rel of gitFiles) yield
|
|
1404
|
+
for (const rel of gitFiles) yield path8.join(root, rel);
|
|
1374
1405
|
return;
|
|
1375
1406
|
}
|
|
1376
1407
|
yield* walkSourceFiles(root, include, exclude);
|
|
@@ -1397,11 +1428,11 @@ async function* walkSourceFiles(dir, include, exclude) {
|
|
|
1397
1428
|
if (entry.isDirectory()) continue;
|
|
1398
1429
|
}
|
|
1399
1430
|
if (exclude.has(entry.name)) continue;
|
|
1400
|
-
const full =
|
|
1431
|
+
const full = path8.join(dir, entry.name);
|
|
1401
1432
|
if (entry.isDirectory()) {
|
|
1402
1433
|
yield* walkSourceFiles(full, include, exclude);
|
|
1403
1434
|
} else if (entry.isFile()) {
|
|
1404
|
-
const ext =
|
|
1435
|
+
const ext = path8.extname(entry.name).toLowerCase();
|
|
1405
1436
|
if (include.has(ext) && !TEST_FILE_RE.test(entry.name)) yield full;
|
|
1406
1437
|
}
|
|
1407
1438
|
}
|
|
@@ -1412,7 +1443,7 @@ function isIncludedSourcePath(rel, include, exclude) {
|
|
|
1412
1443
|
const parts = normalized.split("/");
|
|
1413
1444
|
if (parts.some((part) => exclude.has(part))) return false;
|
|
1414
1445
|
const base = parts.at(-1) ?? "";
|
|
1415
|
-
const ext =
|
|
1446
|
+
const ext = path8.extname(base).toLowerCase();
|
|
1416
1447
|
return include.has(ext) && !TEST_FILE_RE.test(base);
|
|
1417
1448
|
}
|
|
1418
1449
|
var EXPORT_RE = /^export\s+(?:default\s+)?(async\s+)?(function|class|interface|type|const|let|var|enum)\s+(\*?)\s*([A-Za-z_$][\w$]*)/gm;
|
|
@@ -1626,10 +1657,10 @@ function queryCodeMap(map, options) {
|
|
|
1626
1657
|
}
|
|
1627
1658
|
|
|
1628
1659
|
// src/config.ts
|
|
1629
|
-
import { existsSync as
|
|
1660
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1630
1661
|
import { readFileSync } from "fs";
|
|
1631
|
-
import { readFile as
|
|
1632
|
-
import
|
|
1662
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
1663
|
+
import path9 from "path";
|
|
1633
1664
|
var CONFIG_FILE = "haive.config.json";
|
|
1634
1665
|
var DEFAULT_CONFIG = {
|
|
1635
1666
|
autopilot: false,
|
|
@@ -1701,13 +1732,13 @@ function antiPatternGateParams(gate) {
|
|
|
1701
1732
|
}
|
|
1702
1733
|
}
|
|
1703
1734
|
function configPath(paths) {
|
|
1704
|
-
return
|
|
1735
|
+
return path9.join(paths.haiveDir, CONFIG_FILE);
|
|
1705
1736
|
}
|
|
1706
1737
|
async function loadConfig(paths) {
|
|
1707
1738
|
const file = configPath(paths);
|
|
1708
|
-
if (!
|
|
1739
|
+
if (!existsSync7(file)) return { ...DEFAULT_CONFIG };
|
|
1709
1740
|
try {
|
|
1710
|
-
const raw = await
|
|
1741
|
+
const raw = await readFile7(file, "utf8");
|
|
1711
1742
|
const parsed = JSON.parse(raw);
|
|
1712
1743
|
const merged = mergeConfig(DEFAULT_CONFIG, parsed);
|
|
1713
1744
|
if (merged.autopilot) {
|
|
@@ -1720,7 +1751,7 @@ async function loadConfig(paths) {
|
|
|
1720
1751
|
}
|
|
1721
1752
|
function loadConfigSync(paths) {
|
|
1722
1753
|
const file = configPath(paths);
|
|
1723
|
-
if (!
|
|
1754
|
+
if (!existsSync7(file)) return { ...DEFAULT_CONFIG };
|
|
1724
1755
|
try {
|
|
1725
1756
|
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
1726
1757
|
const merged = mergeConfig(DEFAULT_CONFIG, parsed);
|
|
@@ -1730,7 +1761,7 @@ function loadConfigSync(paths) {
|
|
|
1730
1761
|
}
|
|
1731
1762
|
}
|
|
1732
1763
|
async function saveConfig(paths, config) {
|
|
1733
|
-
await
|
|
1764
|
+
await writeFile4(configPath(paths), JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
1734
1765
|
}
|
|
1735
1766
|
function mergeConfig(base, override) {
|
|
1736
1767
|
return {
|
|
@@ -1748,21 +1779,21 @@ function mergeConfig(base, override) {
|
|
|
1748
1779
|
}
|
|
1749
1780
|
|
|
1750
1781
|
// src/cross-repo.ts
|
|
1751
|
-
import { existsSync as
|
|
1752
|
-
import { mkdir as
|
|
1753
|
-
import
|
|
1782
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1783
|
+
import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
1784
|
+
import path10 from "path";
|
|
1754
1785
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1755
1786
|
async function loadImportMap(cacheDir) {
|
|
1756
|
-
const mapPath =
|
|
1757
|
-
if (!
|
|
1787
|
+
const mapPath = path10.join(cacheDir, "import-map.json");
|
|
1788
|
+
if (!existsSync8(mapPath)) return {};
|
|
1758
1789
|
try {
|
|
1759
|
-
return JSON.parse(await
|
|
1790
|
+
return JSON.parse(await readFile8(mapPath, "utf8"));
|
|
1760
1791
|
} catch {
|
|
1761
1792
|
return {};
|
|
1762
1793
|
}
|
|
1763
1794
|
}
|
|
1764
1795
|
async function saveImportMap(cacheDir, map) {
|
|
1765
|
-
await
|
|
1796
|
+
await writeFile5(path10.join(cacheDir, "import-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
1766
1797
|
}
|
|
1767
1798
|
async function pullCrossRepoSources(paths, config, projectRoot) {
|
|
1768
1799
|
const sources = config.crossRepoSources ?? [];
|
|
@@ -1783,8 +1814,8 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1783
1814
|
};
|
|
1784
1815
|
let sourceRoot = null;
|
|
1785
1816
|
if (source.path) {
|
|
1786
|
-
const resolved =
|
|
1787
|
-
if (!
|
|
1817
|
+
const resolved = path10.resolve(projectRoot, source.path);
|
|
1818
|
+
if (!existsSync8(resolved)) {
|
|
1788
1819
|
report.errors.push(`Path not found: ${resolved}`);
|
|
1789
1820
|
return report;
|
|
1790
1821
|
}
|
|
@@ -1797,7 +1828,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1797
1828
|
return report;
|
|
1798
1829
|
}
|
|
1799
1830
|
const sourcePaths = resolveHaivePaths(sourceRoot);
|
|
1800
|
-
if (!
|
|
1831
|
+
if (!existsSync8(sourcePaths.memoriesDir)) {
|
|
1801
1832
|
report.errors.push(`No .ai/memories/ found at ${sourceRoot}`);
|
|
1802
1833
|
return report;
|
|
1803
1834
|
}
|
|
@@ -1820,10 +1851,10 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1820
1851
|
report.skipped.push("no shared memories found in source");
|
|
1821
1852
|
return report;
|
|
1822
1853
|
}
|
|
1823
|
-
const destDir =
|
|
1824
|
-
await
|
|
1825
|
-
const cacheDir =
|
|
1826
|
-
await
|
|
1854
|
+
const destDir = path10.join(paths.memoriesDir, "shared", source.name);
|
|
1855
|
+
await mkdir5(destDir, { recursive: true });
|
|
1856
|
+
const cacheDir = path10.join(paths.haiveDir, ".cache", "cross-repo", source.name);
|
|
1857
|
+
await mkdir5(cacheDir, { recursive: true });
|
|
1827
1858
|
const importMap = await loadImportMap(cacheDir);
|
|
1828
1859
|
const mapDirty = false;
|
|
1829
1860
|
let dirty = mapDirty;
|
|
@@ -1837,7 +1868,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1837
1868
|
|
|
1838
1869
|
`;
|
|
1839
1870
|
const existingLocalPath = importMap[sourceId];
|
|
1840
|
-
if (existingLocalPath &&
|
|
1871
|
+
if (existingLocalPath && existsSync8(existingLocalPath)) {
|
|
1841
1872
|
const existingFiles = await loadMemoriesFromDir(destDir);
|
|
1842
1873
|
const existingEntry = existingFiles.find(({ filePath }) => filePath === existingLocalPath);
|
|
1843
1874
|
const sourceBodyStripped = memory.body.trim();
|
|
@@ -1848,7 +1879,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1848
1879
|
}
|
|
1849
1880
|
const updatedBody = importedBodyPrefix + memory.body;
|
|
1850
1881
|
if (existingEntry) {
|
|
1851
|
-
await
|
|
1882
|
+
await writeFile5(
|
|
1852
1883
|
existingLocalPath,
|
|
1853
1884
|
serializeMemory({ frontmatter: existingEntry.memory.frontmatter, body: updatedBody }),
|
|
1854
1885
|
"utf8"
|
|
@@ -1872,8 +1903,8 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1872
1903
|
topic: fm.topic ? `${source.name}:${fm.topic}` : void 0
|
|
1873
1904
|
});
|
|
1874
1905
|
const body = importedBodyPrefix + memory.body;
|
|
1875
|
-
const destPath =
|
|
1876
|
-
await
|
|
1906
|
+
const destPath = path10.join(destDir, `${newFm.id}.md`);
|
|
1907
|
+
await writeFile5(destPath, serializeMemory({ frontmatter: newFm, body }), "utf8");
|
|
1877
1908
|
importMap[sourceId] = destPath;
|
|
1878
1909
|
dirty = true;
|
|
1879
1910
|
report.imported.push(sourceId);
|
|
@@ -1883,9 +1914,9 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
1883
1914
|
return report;
|
|
1884
1915
|
}
|
|
1885
1916
|
async function cloneOrFetchGitSource(source, paths, report) {
|
|
1886
|
-
const cacheDir =
|
|
1887
|
-
await
|
|
1888
|
-
if (
|
|
1917
|
+
const cacheDir = path10.join(paths.haiveDir, ".cache", "cross-repo", source.name);
|
|
1918
|
+
await mkdir5(cacheDir, { recursive: true });
|
|
1919
|
+
if (existsSync8(path10.join(cacheDir, ".git"))) {
|
|
1889
1920
|
const result = spawnSync2("git", ["fetch", "--depth=1", "origin"], {
|
|
1890
1921
|
cwd: cacheDir,
|
|
1891
1922
|
encoding: "utf8"
|
|
@@ -1910,9 +1941,9 @@ async function cloneOrFetchGitSource(source, paths, report) {
|
|
|
1910
1941
|
}
|
|
1911
1942
|
|
|
1912
1943
|
// src/dep-tracker.ts
|
|
1913
|
-
import { existsSync as
|
|
1914
|
-
import { readFile as
|
|
1915
|
-
import
|
|
1944
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1945
|
+
import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
1946
|
+
import path11 from "path";
|
|
1916
1947
|
function parsePackageJson(content) {
|
|
1917
1948
|
try {
|
|
1918
1949
|
const pkg = JSON.parse(content);
|
|
@@ -1984,7 +2015,7 @@ var KNOWN_MANIFESTS = [
|
|
|
1984
2015
|
{ name: "pom.xml", parser: parsePomXml }
|
|
1985
2016
|
];
|
|
1986
2017
|
function getParser(file) {
|
|
1987
|
-
const base =
|
|
2018
|
+
const base = path11.basename(file);
|
|
1988
2019
|
return KNOWN_MANIFESTS.find((m) => m.name === base)?.parser ?? null;
|
|
1989
2020
|
}
|
|
1990
2021
|
function extractMajor(version) {
|
|
@@ -2002,32 +2033,32 @@ function isMajorBump(from, to) {
|
|
|
2002
2033
|
}
|
|
2003
2034
|
function resolveManifestFiles(projectRoot, configuredFiles) {
|
|
2004
2035
|
if (configuredFiles !== void 0) {
|
|
2005
|
-
return configuredFiles.map((f) =>
|
|
2036
|
+
return configuredFiles.map((f) => path11.resolve(projectRoot, f)).filter(existsSync9);
|
|
2006
2037
|
}
|
|
2007
|
-
return KNOWN_MANIFESTS.map(({ name }) =>
|
|
2038
|
+
return KNOWN_MANIFESTS.map(({ name }) => path11.join(projectRoot, name)).filter(existsSync9);
|
|
2008
2039
|
}
|
|
2009
2040
|
async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
|
|
2010
|
-
const contractsDir =
|
|
2011
|
-
await
|
|
2041
|
+
const contractsDir = path11.join(haiveDir, "contracts");
|
|
2042
|
+
await mkdir6(contractsDir, { recursive: true });
|
|
2012
2043
|
const results = [];
|
|
2013
2044
|
for (const manifestPath of manifestFiles) {
|
|
2014
2045
|
const parser = getParser(manifestPath);
|
|
2015
2046
|
if (!parser) continue;
|
|
2016
|
-
const content = await
|
|
2047
|
+
const content = await readFile9(manifestPath, "utf8");
|
|
2017
2048
|
const currentDeps = parser(content);
|
|
2018
|
-
const lockName = `deps-${
|
|
2019
|
-
const lockPath =
|
|
2020
|
-
if (!
|
|
2049
|
+
const lockName = `deps-${path11.basename(manifestPath)}.lock`;
|
|
2050
|
+
const lockPath = path11.join(contractsDir, lockName);
|
|
2051
|
+
if (!existsSync9(lockPath)) {
|
|
2021
2052
|
const snapshot2 = {
|
|
2022
|
-
file:
|
|
2023
|
-
format:
|
|
2053
|
+
file: path11.relative(projectRoot, manifestPath),
|
|
2054
|
+
format: path11.basename(manifestPath),
|
|
2024
2055
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2025
2056
|
deps: currentDeps
|
|
2026
2057
|
};
|
|
2027
|
-
await
|
|
2058
|
+
await writeFile6(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
|
|
2028
2059
|
continue;
|
|
2029
2060
|
}
|
|
2030
|
-
const snapshot = JSON.parse(await
|
|
2061
|
+
const snapshot = JSON.parse(await readFile9(lockPath, "utf8"));
|
|
2031
2062
|
const changes = [];
|
|
2032
2063
|
for (const [name, currentVer] of Object.entries(currentDeps)) {
|
|
2033
2064
|
const prevVer = snapshot.deps[name];
|
|
@@ -2041,22 +2072,22 @@ async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
|
|
|
2041
2072
|
}
|
|
2042
2073
|
}
|
|
2043
2074
|
if (changes.length > 0) {
|
|
2044
|
-
results.push({ file:
|
|
2075
|
+
results.push({ file: path11.relative(projectRoot, manifestPath), changes });
|
|
2045
2076
|
const updated = {
|
|
2046
2077
|
...snapshot,
|
|
2047
2078
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2048
2079
|
deps: currentDeps
|
|
2049
2080
|
};
|
|
2050
|
-
await
|
|
2081
|
+
await writeFile6(lockPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
2051
2082
|
}
|
|
2052
2083
|
}
|
|
2053
2084
|
return results;
|
|
2054
2085
|
}
|
|
2055
2086
|
|
|
2056
2087
|
// src/contract-watcher.ts
|
|
2057
|
-
import { existsSync as
|
|
2058
|
-
import { readFile as
|
|
2059
|
-
import
|
|
2088
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2089
|
+
import { readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
2090
|
+
import path12 from "path";
|
|
2060
2091
|
import crypto from "crypto";
|
|
2061
2092
|
function sha256(content) {
|
|
2062
2093
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
@@ -2259,14 +2290,14 @@ function diffSnapshots(before, after) {
|
|
|
2259
2290
|
return changes;
|
|
2260
2291
|
}
|
|
2261
2292
|
function contractLockPath(haiveDir, name) {
|
|
2262
|
-
return
|
|
2293
|
+
return path12.join(haiveDir, "contracts", `${name}.lock`);
|
|
2263
2294
|
}
|
|
2264
2295
|
async function snapshotContract(projectRoot, haiveDir, contract) {
|
|
2265
|
-
const filePath =
|
|
2266
|
-
if (!
|
|
2296
|
+
const filePath = path12.resolve(projectRoot, contract.path);
|
|
2297
|
+
if (!existsSync10(filePath)) {
|
|
2267
2298
|
throw new Error(`Contract file not found: ${filePath}`);
|
|
2268
2299
|
}
|
|
2269
|
-
const content = await
|
|
2300
|
+
const content = await readFile10(filePath, "utf8");
|
|
2270
2301
|
const parsed = parseByFormat(content, contract.format, filePath);
|
|
2271
2302
|
const snapshot = {
|
|
2272
2303
|
name: contract.name,
|
|
@@ -2276,23 +2307,23 @@ async function snapshotContract(projectRoot, haiveDir, contract) {
|
|
|
2276
2307
|
hash: sha256(content),
|
|
2277
2308
|
...parsed
|
|
2278
2309
|
};
|
|
2279
|
-
const contractsDir =
|
|
2280
|
-
await
|
|
2281
|
-
await
|
|
2310
|
+
const contractsDir = path12.join(haiveDir, "contracts");
|
|
2311
|
+
await mkdir7(contractsDir, { recursive: true });
|
|
2312
|
+
await writeFile7(contractLockPath(haiveDir, contract.name), JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
2282
2313
|
return snapshot;
|
|
2283
2314
|
}
|
|
2284
2315
|
async function diffContract(projectRoot, haiveDir, contract) {
|
|
2285
|
-
const filePath =
|
|
2286
|
-
if (!
|
|
2316
|
+
const filePath = path12.resolve(projectRoot, contract.path);
|
|
2317
|
+
if (!existsSync10(filePath)) {
|
|
2287
2318
|
return { contract: contract.name, file: contract.path, changes: [], unchanged: true };
|
|
2288
2319
|
}
|
|
2289
2320
|
const lockPath = contractLockPath(haiveDir, contract.name);
|
|
2290
|
-
if (!
|
|
2321
|
+
if (!existsSync10(lockPath)) {
|
|
2291
2322
|
await snapshotContract(projectRoot, haiveDir, contract);
|
|
2292
2323
|
return { contract: contract.name, file: contract.path, changes: [], unchanged: true };
|
|
2293
2324
|
}
|
|
2294
|
-
const content = await
|
|
2295
|
-
const beforeSnapshot = JSON.parse(await
|
|
2325
|
+
const content = await readFile10(filePath, "utf8");
|
|
2326
|
+
const beforeSnapshot = JSON.parse(await readFile10(lockPath, "utf8"));
|
|
2296
2327
|
const afterParsed = parseByFormat(content, contract.format, filePath);
|
|
2297
2328
|
const afterSnapshot = {
|
|
2298
2329
|
...beforeSnapshot,
|
|
@@ -2302,7 +2333,7 @@ async function diffContract(projectRoot, haiveDir, contract) {
|
|
|
2302
2333
|
};
|
|
2303
2334
|
const changes = diffSnapshots(beforeSnapshot, afterSnapshot);
|
|
2304
2335
|
if (changes.length > 0) {
|
|
2305
|
-
await
|
|
2336
|
+
await writeFile7(lockPath, JSON.stringify(afterSnapshot, null, 2) + "\n", "utf8");
|
|
2306
2337
|
}
|
|
2307
2338
|
return {
|
|
2308
2339
|
contract: contract.name,
|
|
@@ -2320,27 +2351,27 @@ async function watchContracts(projectRoot, haiveDir, contractFiles) {
|
|
|
2320
2351
|
}
|
|
2321
2352
|
|
|
2322
2353
|
// src/usage-log.ts
|
|
2323
|
-
import { appendFile as appendFile2, mkdir as
|
|
2324
|
-
import { existsSync as
|
|
2325
|
-
import
|
|
2354
|
+
import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile11, stat as stat2 } from "fs/promises";
|
|
2355
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2356
|
+
import path13 from "path";
|
|
2326
2357
|
var USAGE_LOG_FILE = "tool-usage.jsonl";
|
|
2327
2358
|
var USAGE_LOG_DIR = ".usage";
|
|
2328
2359
|
function usageLogPath(paths) {
|
|
2329
|
-
return
|
|
2360
|
+
return path13.join(paths.haiveDir, USAGE_LOG_DIR, USAGE_LOG_FILE);
|
|
2330
2361
|
}
|
|
2331
2362
|
async function appendUsageEvent(paths, event) {
|
|
2332
2363
|
try {
|
|
2333
2364
|
const file = usageLogPath(paths);
|
|
2334
|
-
const dir =
|
|
2335
|
-
if (!
|
|
2365
|
+
const dir = path13.dirname(file);
|
|
2366
|
+
if (!existsSync11(dir)) await mkdir8(dir, { recursive: true });
|
|
2336
2367
|
await appendFile2(file, JSON.stringify(event) + "\n", "utf8");
|
|
2337
2368
|
} catch {
|
|
2338
2369
|
}
|
|
2339
2370
|
}
|
|
2340
2371
|
async function readUsageEvents(paths) {
|
|
2341
2372
|
const file = usageLogPath(paths);
|
|
2342
|
-
if (!
|
|
2343
|
-
const raw = await
|
|
2373
|
+
if (!existsSync11(file)) return [];
|
|
2374
|
+
const raw = await readFile11(file, "utf8");
|
|
2344
2375
|
const out = [];
|
|
2345
2376
|
for (const line of raw.split("\n")) {
|
|
2346
2377
|
if (!line) continue;
|
|
@@ -2389,9 +2420,9 @@ function parseSince(input) {
|
|
|
2389
2420
|
}
|
|
2390
2421
|
async function usageLogSize(paths) {
|
|
2391
2422
|
const file = usageLogPath(paths);
|
|
2392
|
-
if (!
|
|
2423
|
+
if (!existsSync11(file)) return { exists: false, size_bytes: 0, lines: 0 };
|
|
2393
2424
|
const st = await stat2(file);
|
|
2394
|
-
const raw = await
|
|
2425
|
+
const raw = await readFile11(file, "utf8");
|
|
2395
2426
|
return { exists: true, size_bytes: st.size, lines: raw.split("\n").filter((l) => l).length };
|
|
2396
2427
|
}
|
|
2397
2428
|
|
|
@@ -2461,21 +2492,21 @@ function extractActionsBriefBody(markdown, maxChars = MAX_DEFAULT_CHARS) {
|
|
|
2461
2492
|
}
|
|
2462
2493
|
|
|
2463
2494
|
// src/resolve-project.ts
|
|
2464
|
-
import { existsSync as
|
|
2465
|
-
import
|
|
2495
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2496
|
+
import path14 from "path";
|
|
2466
2497
|
var ROOT_MARKERS2 = [".ai", ".git", "package.json"];
|
|
2467
2498
|
function markersAtRoot(root) {
|
|
2468
2499
|
const found = [];
|
|
2469
2500
|
for (const m of ROOT_MARKERS2) {
|
|
2470
|
-
if (
|
|
2501
|
+
if (existsSync12(path14.join(root, m))) found.push(m);
|
|
2471
2502
|
}
|
|
2472
2503
|
return found;
|
|
2473
2504
|
}
|
|
2474
2505
|
function resolveProjectInfo(opts = {}) {
|
|
2475
2506
|
const env = opts.env ?? process.env;
|
|
2476
|
-
const cwd =
|
|
2507
|
+
const cwd = path14.resolve(opts.cwd ?? process.cwd());
|
|
2477
2508
|
const raw = env.HAIVE_PROJECT_ROOT;
|
|
2478
|
-
const explicit = raw !== void 0 && raw !== "" ?
|
|
2509
|
+
const explicit = raw !== void 0 && raw !== "" ? path14.resolve(raw) : null;
|
|
2479
2510
|
const resolvedRoot = explicit ?? findProjectRoot(cwd);
|
|
2480
2511
|
const paths = resolveHaivePaths(resolvedRoot);
|
|
2481
2512
|
return {
|
|
@@ -2483,8 +2514,8 @@ function resolveProjectInfo(opts = {}) {
|
|
|
2483
2514
|
resolved_root: resolvedRoot,
|
|
2484
2515
|
haive_project_root_env: explicit,
|
|
2485
2516
|
explicit_root: explicit != null,
|
|
2486
|
-
haive_dir_exists:
|
|
2487
|
-
memories_dir_exists:
|
|
2517
|
+
haive_dir_exists: existsSync12(paths.haiveDir),
|
|
2518
|
+
memories_dir_exists: existsSync12(paths.memoriesDir),
|
|
2488
2519
|
runtime_dir: paths.runtimeDir,
|
|
2489
2520
|
markers_found: markersAtRoot(resolvedRoot)
|
|
2490
2521
|
};
|
|
@@ -2739,16 +2770,16 @@ function findLexicalConflictPairs(memories, opts) {
|
|
|
2739
2770
|
}
|
|
2740
2771
|
|
|
2741
2772
|
// src/runtime-journal.ts
|
|
2742
|
-
import { mkdir as
|
|
2743
|
-
import { existsSync as
|
|
2744
|
-
import
|
|
2773
|
+
import { mkdir as mkdir9, readFile as readFile12, appendFile as appendFile3 } from "fs/promises";
|
|
2774
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2775
|
+
import path15 from "path";
|
|
2745
2776
|
var RUNTIME_JOURNAL_FILENAME = "session-journal.ndjson";
|
|
2746
2777
|
function runtimeJournalPath(paths) {
|
|
2747
|
-
return
|
|
2778
|
+
return path15.join(paths.runtimeDir, RUNTIME_JOURNAL_FILENAME);
|
|
2748
2779
|
}
|
|
2749
2780
|
async function appendRuntimeJournalEntry(paths, entry) {
|
|
2750
2781
|
try {
|
|
2751
|
-
await
|
|
2782
|
+
await mkdir9(paths.runtimeDir, { recursive: true });
|
|
2752
2783
|
const line = {
|
|
2753
2784
|
ts: entry.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2754
2785
|
kind: entry.kind,
|
|
@@ -2766,9 +2797,9 @@ async function appendRuntimeJournalEntry(paths, entry) {
|
|
|
2766
2797
|
}
|
|
2767
2798
|
async function readRuntimeJournalTail(paths, limit) {
|
|
2768
2799
|
const file = runtimeJournalPath(paths);
|
|
2769
|
-
if (!
|
|
2800
|
+
if (!existsSync13(file) || limit <= 0) return [];
|
|
2770
2801
|
try {
|
|
2771
|
-
const raw = await
|
|
2802
|
+
const raw = await readFile12(file, "utf8");
|
|
2772
2803
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
2773
2804
|
const parsed = [];
|
|
2774
2805
|
for (const line of lines.slice(-limit)) {
|
|
@@ -2784,22 +2815,22 @@ async function readRuntimeJournalTail(paths, limit) {
|
|
|
2784
2815
|
}
|
|
2785
2816
|
|
|
2786
2817
|
// src/enforcement.ts
|
|
2787
|
-
import { mkdir as
|
|
2788
|
-
import { existsSync as
|
|
2789
|
-
import
|
|
2818
|
+
import { mkdir as mkdir10, readdir as readdir4, readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
|
|
2819
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2820
|
+
import path16 from "path";
|
|
2790
2821
|
var BRIEFING_MARKER_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
2791
2822
|
var SESSION_RECAP_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2792
2823
|
function enforcementDir(paths) {
|
|
2793
|
-
return
|
|
2824
|
+
return path16.join(paths.runtimeDir, "enforcement");
|
|
2794
2825
|
}
|
|
2795
2826
|
function briefingMarkersDir(paths) {
|
|
2796
|
-
return
|
|
2827
|
+
return path16.join(enforcementDir(paths), "briefings");
|
|
2797
2828
|
}
|
|
2798
2829
|
function normalizeSessionId(sessionId) {
|
|
2799
2830
|
return (sessionId?.trim() || "default").replace(/[^a-zA-Z0-9_.-]+/g, "-").slice(0, 120);
|
|
2800
2831
|
}
|
|
2801
2832
|
function briefingMarkerPath(paths, sessionId) {
|
|
2802
|
-
return
|
|
2833
|
+
return path16.join(briefingMarkersDir(paths), `${normalizeSessionId(sessionId)}.json`);
|
|
2803
2834
|
}
|
|
2804
2835
|
async function writeBriefingMarker(paths, input) {
|
|
2805
2836
|
const marker = {
|
|
@@ -2811,8 +2842,8 @@ async function writeBriefingMarker(paths, input) {
|
|
|
2811
2842
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2812
2843
|
root: paths.root
|
|
2813
2844
|
};
|
|
2814
|
-
await
|
|
2815
|
-
await
|
|
2845
|
+
await mkdir10(briefingMarkersDir(paths), { recursive: true });
|
|
2846
|
+
await writeFile8(
|
|
2816
2847
|
briefingMarkerPath(paths, marker.session_id),
|
|
2817
2848
|
JSON.stringify(marker, null, 2) + "\n",
|
|
2818
2849
|
"utf8"
|
|
@@ -2823,18 +2854,18 @@ async function hasRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKER
|
|
|
2823
2854
|
const now = Date.now();
|
|
2824
2855
|
const candidates = [];
|
|
2825
2856
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
2826
|
-
if (
|
|
2857
|
+
if (existsSync14(exact)) candidates.push(exact);
|
|
2827
2858
|
try {
|
|
2828
2859
|
const dir = briefingMarkersDir(paths);
|
|
2829
2860
|
const files = await readdir4(dir);
|
|
2830
2861
|
for (const file of files) {
|
|
2831
|
-
if (file.endsWith(".json")) candidates.push(
|
|
2862
|
+
if (file.endsWith(".json")) candidates.push(path16.join(dir, file));
|
|
2832
2863
|
}
|
|
2833
2864
|
} catch {
|
|
2834
2865
|
}
|
|
2835
2866
|
for (const file of new Set(candidates)) {
|
|
2836
2867
|
try {
|
|
2837
|
-
const marker = JSON.parse(await
|
|
2868
|
+
const marker = JSON.parse(await readFile13(file, "utf8"));
|
|
2838
2869
|
const created = Date.parse(marker.created_at);
|
|
2839
2870
|
if (Number.isFinite(created) && now - created <= ttlMs) return true;
|
|
2840
2871
|
} catch {
|
|
@@ -2846,12 +2877,12 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
2846
2877
|
const now = Date.now();
|
|
2847
2878
|
const candidates = [];
|
|
2848
2879
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
2849
|
-
if (
|
|
2880
|
+
if (existsSync14(exact)) candidates.push(exact);
|
|
2850
2881
|
try {
|
|
2851
2882
|
const dir = briefingMarkersDir(paths);
|
|
2852
2883
|
const files = await readdir4(dir);
|
|
2853
2884
|
for (const file of files) {
|
|
2854
|
-
if (file.endsWith(".json")) candidates.push(
|
|
2885
|
+
if (file.endsWith(".json")) candidates.push(path16.join(dir, file));
|
|
2855
2886
|
}
|
|
2856
2887
|
} catch {
|
|
2857
2888
|
}
|
|
@@ -2859,7 +2890,7 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
2859
2890
|
let freshestTs = 0;
|
|
2860
2891
|
for (const file of new Set(candidates)) {
|
|
2861
2892
|
try {
|
|
2862
|
-
const marker = JSON.parse(await
|
|
2893
|
+
const marker = JSON.parse(await readFile13(file, "utf8"));
|
|
2863
2894
|
const created = Date.parse(marker.created_at);
|
|
2864
2895
|
if (!Number.isFinite(created) || now - created > ttlMs) continue;
|
|
2865
2896
|
if (created > freshestTs) {
|
|
@@ -2912,10 +2943,10 @@ function isRetiredMemory(fm, body = "", now = /* @__PURE__ */ new Date()) {
|
|
|
2912
2943
|
function normalizeProjectPath(value) {
|
|
2913
2944
|
return value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^[ab]\//, "").replace(/\/+$/g, "");
|
|
2914
2945
|
}
|
|
2915
|
-
function sensorAppliesToPath(sensor, anchorPaths,
|
|
2946
|
+
function sensorAppliesToPath(sensor, anchorPaths, path17) {
|
|
2916
2947
|
const scopes = sensor.paths.length > 0 ? sensor.paths : anchorPaths;
|
|
2917
2948
|
if (scopes.length === 0) return true;
|
|
2918
|
-
const target = normalizeProjectPath(
|
|
2949
|
+
const target = normalizeProjectPath(path17);
|
|
2919
2950
|
return scopes.some((rawScope) => {
|
|
2920
2951
|
const scope = normalizeProjectPath(rawScope);
|
|
2921
2952
|
if (!scope) return false;
|
|
@@ -3189,8 +3220,8 @@ function normalizeFindingSeverity(raw) {
|
|
|
3189
3220
|
return "info";
|
|
3190
3221
|
}
|
|
3191
3222
|
}
|
|
3192
|
-
function findingKey(tool, ruleId,
|
|
3193
|
-
return `${tool}:${ruleId}:${
|
|
3223
|
+
function findingKey(tool, ruleId, path17) {
|
|
3224
|
+
return `${tool}:${ruleId}:${path17}`;
|
|
3194
3225
|
}
|
|
3195
3226
|
function coerceJson(input) {
|
|
3196
3227
|
if (typeof input === "string") {
|
|
@@ -3224,8 +3255,8 @@ function parseSarif(input) {
|
|
|
3224
3255
|
const physical = asRecord(location.physicalLocation);
|
|
3225
3256
|
const artifact = asRecord(physical.artifactLocation);
|
|
3226
3257
|
const region = asRecord(physical.region);
|
|
3227
|
-
const
|
|
3228
|
-
if (!
|
|
3258
|
+
const path17 = typeof artifact.uri === "string" ? normalizeUri(artifact.uri) : "";
|
|
3259
|
+
if (!path17) continue;
|
|
3229
3260
|
const line = typeof region.startLine === "number" ? region.startLine : void 0;
|
|
3230
3261
|
const snippet = typeof asRecord(region.snippet).text === "string" ? asRecord(region.snippet).text.trim() : void 0;
|
|
3231
3262
|
findings.push({
|
|
@@ -3233,10 +3264,10 @@ function parseSarif(input) {
|
|
|
3233
3264
|
ruleId,
|
|
3234
3265
|
message: message.trim(),
|
|
3235
3266
|
severity,
|
|
3236
|
-
path:
|
|
3267
|
+
path: path17,
|
|
3237
3268
|
...line !== void 0 ? { line } : {},
|
|
3238
3269
|
...snippet ? { snippet } : {},
|
|
3239
|
-
key: findingKey(tool, ruleId,
|
|
3270
|
+
key: findingKey(tool, ruleId, path17)
|
|
3240
3271
|
});
|
|
3241
3272
|
}
|
|
3242
3273
|
}
|
|
@@ -3255,17 +3286,17 @@ function parseSonar(input) {
|
|
|
3255
3286
|
(typeof issue.severity === "string" ? issue.severity : void 0) ?? impactSeverity
|
|
3256
3287
|
);
|
|
3257
3288
|
const component = typeof issue.component === "string" ? issue.component : "";
|
|
3258
|
-
const
|
|
3259
|
-
if (!
|
|
3289
|
+
const path17 = componentToPath(component);
|
|
3290
|
+
if (!path17) continue;
|
|
3260
3291
|
const line = typeof issue.line === "number" ? issue.line : void 0;
|
|
3261
3292
|
findings.push({
|
|
3262
3293
|
tool: "sonar",
|
|
3263
3294
|
ruleId,
|
|
3264
3295
|
message,
|
|
3265
3296
|
severity,
|
|
3266
|
-
path:
|
|
3297
|
+
path: path17,
|
|
3267
3298
|
...line !== void 0 ? { line } : {},
|
|
3268
|
-
key: findingKey("sonar", ruleId,
|
|
3299
|
+
key: findingKey("sonar", ruleId, path17)
|
|
3269
3300
|
});
|
|
3270
3301
|
}
|
|
3271
3302
|
return findings;
|
|
@@ -3516,6 +3547,7 @@ export {
|
|
|
3516
3547
|
MemoryTypeSchema,
|
|
3517
3548
|
PREVENTION_DEBOUNCE_MS,
|
|
3518
3549
|
PROJECT_CONTEXT_FILE,
|
|
3550
|
+
PROJECT_CONTEXT_THROTTLE_MS,
|
|
3519
3551
|
RUNTIME_JOURNAL_FILENAME,
|
|
3520
3552
|
SESSION_RECAP_TTL_MS,
|
|
3521
3553
|
STACK_PACK_TAG,
|
|
@@ -3572,6 +3604,7 @@ export {
|
|
|
3572
3604
|
getUsage,
|
|
3573
3605
|
globToRegExp,
|
|
3574
3606
|
hasRecentBriefingMarker,
|
|
3607
|
+
hashProjectContext,
|
|
3575
3608
|
inferModulesFromPaths,
|
|
3576
3609
|
isAutoPromoteEligible,
|
|
3577
3610
|
isDecaying,
|
|
@@ -3607,6 +3640,7 @@ export {
|
|
|
3607
3640
|
pathsOverlap,
|
|
3608
3641
|
pickSnippetNeedle,
|
|
3609
3642
|
preventionLogPath,
|
|
3643
|
+
projectContextRecentlyEmitted,
|
|
3610
3644
|
pullCrossRepoSources,
|
|
3611
3645
|
queryCodeMap,
|
|
3612
3646
|
rankMemoriesLexical,
|
|
@@ -3615,6 +3649,7 @@ export {
|
|
|
3615
3649
|
readUsageEvents,
|
|
3616
3650
|
recordApplied,
|
|
3617
3651
|
recordPrevention,
|
|
3652
|
+
recordProjectContextEmission,
|
|
3618
3653
|
recordRejection,
|
|
3619
3654
|
relPathFrom,
|
|
3620
3655
|
resolveBriefingBudget,
|