@hivelore/core 0.54.0 → 0.56.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 +253 -1
- package/dist/index.js +478 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1042,6 +1042,26 @@ function renderPreventionReceiptShare(receipt) {
|
|
|
1042
1042
|
lines.push("", `<sub>${HIVELORE_ATTRIBUTION}</sub>`);
|
|
1043
1043
|
return lines.join("\n");
|
|
1044
1044
|
}
|
|
1045
|
+
var PREVENTION_RECEIPT_MARKER = "<!-- haive:prevention-receipt -->";
|
|
1046
|
+
function renderPreventionComment(receipt, findings = []) {
|
|
1047
|
+
const fired = findings.filter((f) => f.code === "sensor-block" || f.code === "sensor-warn");
|
|
1048
|
+
const lines = [PREVENTION_RECEIPT_MARKER, "", "## Hivelore prevention receipt", ""];
|
|
1049
|
+
if (fired.length === 0) {
|
|
1050
|
+
lines.push("No documented sensor fired on this PR.", "");
|
|
1051
|
+
} else {
|
|
1052
|
+
lines.push("### Fired on this PR", "");
|
|
1053
|
+
for (const f of fired) {
|
|
1054
|
+
const where = f.file ? ` \u2014 \`${f.file}\`` : "";
|
|
1055
|
+
lines.push(`- **${f.memory_ids?.[0] ?? "sensor"}**${where} \u2014 ${f.message ?? "sensor fired"}`);
|
|
1056
|
+
if (f.matched_line) lines.push(` \`\`\`
|
|
1057
|
+
${f.matched_line}
|
|
1058
|
+
\`\`\``);
|
|
1059
|
+
}
|
|
1060
|
+
lines.push("");
|
|
1061
|
+
}
|
|
1062
|
+
lines.push(renderPreventionReceiptShare(receipt));
|
|
1063
|
+
return lines.join("\n");
|
|
1064
|
+
}
|
|
1045
1065
|
async function loadPreventionEvents(paths) {
|
|
1046
1066
|
const file = preventionLogPath(paths);
|
|
1047
1067
|
if (!existsSync4(file)) return [];
|
|
@@ -1204,6 +1224,178 @@ async function recordProjectContextEmission(paths, hash, now = Date.now()) {
|
|
|
1204
1224
|
});
|
|
1205
1225
|
}
|
|
1206
1226
|
|
|
1227
|
+
// src/gate-reminder.ts
|
|
1228
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
1229
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1230
|
+
import path8 from "path";
|
|
1231
|
+
var GATE_REMINDER_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
1232
|
+
function reminderMarkerPath(paths) {
|
|
1233
|
+
return path8.join(paths.haiveDir, ".cache", "gate-reminders.json");
|
|
1234
|
+
}
|
|
1235
|
+
async function readMarkers(paths) {
|
|
1236
|
+
const file = reminderMarkerPath(paths);
|
|
1237
|
+
if (!existsSync6(file)) return {};
|
|
1238
|
+
try {
|
|
1239
|
+
const parsed = JSON.parse(await readFile6(file, "utf8"));
|
|
1240
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1241
|
+
} catch {
|
|
1242
|
+
return {};
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
async function shouldExpandGateReminder(paths, key, now = Date.now(), windowMs = GATE_REMINDER_WINDOW_MS) {
|
|
1246
|
+
const last = (await readMarkers(paths))[key];
|
|
1247
|
+
if (!last) return true;
|
|
1248
|
+
const at = Date.parse(last);
|
|
1249
|
+
return !Number.isFinite(at) || now - at >= windowMs;
|
|
1250
|
+
}
|
|
1251
|
+
async function recordGateReminder(paths, key, now = Date.now()) {
|
|
1252
|
+
const file = reminderMarkerPath(paths);
|
|
1253
|
+
const markers = await readMarkers(paths);
|
|
1254
|
+
markers[key] = new Date(now).toISOString();
|
|
1255
|
+
await mkdir4(path8.dirname(file), { recursive: true }).catch(() => {
|
|
1256
|
+
});
|
|
1257
|
+
await writeFile3(file, JSON.stringify(markers, null, 2), "utf8").catch(() => {
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/gate-verdict.ts
|
|
1262
|
+
var PROCESS_GATE_CODES = /* @__PURE__ */ new Set([
|
|
1263
|
+
"briefing-missing",
|
|
1264
|
+
"session-recap-missing",
|
|
1265
|
+
"decision-coverage-missing",
|
|
1266
|
+
"bootstrap-incomplete"
|
|
1267
|
+
]);
|
|
1268
|
+
var CONTENT_CATCH_CODES = /* @__PURE__ */ new Set(["sensor-block", "precommit-policy-block"]);
|
|
1269
|
+
var CONTENT_CODES = /* @__PURE__ */ new Set([...CONTENT_CATCH_CODES, "sensor-warn"]);
|
|
1270
|
+
var SETUP_GATE_CODES = /* @__PURE__ */ new Set([
|
|
1271
|
+
...PROCESS_GATE_CODES,
|
|
1272
|
+
"enforcement-score-below-threshold"
|
|
1273
|
+
]);
|
|
1274
|
+
var DEFAULT_POSTURE = "balanced";
|
|
1275
|
+
var POSTURE_DEFAULTS = {
|
|
1276
|
+
// Report everything, refuse nothing. For adopting Hivelore on a repo mid-flight.
|
|
1277
|
+
advisory: { mode: "advisory", processGate: "warn", humanCommits: "relaxed" },
|
|
1278
|
+
// Refuse on deterministic, code-bound evidence only. The default.
|
|
1279
|
+
balanced: { mode: "strict", processGate: "warn", humanCommits: "relaxed" },
|
|
1280
|
+
// Process gates bind too, at the sharing points. For teams that want the workflow enforced.
|
|
1281
|
+
strict: { mode: "strict", processGate: "block", humanCommits: "strict" }
|
|
1282
|
+
};
|
|
1283
|
+
function resolveGatePolicy(input) {
|
|
1284
|
+
const cfg = input ?? {};
|
|
1285
|
+
const posture = cfg.posture ?? DEFAULT_POSTURE;
|
|
1286
|
+
const base = POSTURE_DEFAULTS[posture] ?? POSTURE_DEFAULTS[DEFAULT_POSTURE];
|
|
1287
|
+
const overrides = [];
|
|
1288
|
+
for (const key of ["mode", "processGate", "humanCommits"]) {
|
|
1289
|
+
if (cfg[key] !== void 0) overrides.push(key);
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
posture,
|
|
1293
|
+
mode: cfg.mode ?? base.mode,
|
|
1294
|
+
processGate: cfg.processGate ?? base.processGate,
|
|
1295
|
+
humanCommits: cfg.humanCommits ?? base.humanCommits,
|
|
1296
|
+
scoreThreshold: cfg.scoreThreshold ?? 80,
|
|
1297
|
+
overrides
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
function describePosture(policy) {
|
|
1301
|
+
const base = policy.posture === "advisory" ? "reports everything, refuses nothing" : policy.posture === "strict" ? "refuses on deterministic findings AND on process gates at pre-push/CI" : "refuses on deterministic findings only (sensors, anti-patterns, stale anchors on touched files)";
|
|
1302
|
+
const pinned = policy.overrides.length > 0 ? ` \xB7 overridden: ${policy.overrides.join(", ")}` : "";
|
|
1303
|
+
return `${policy.posture} \u2014 ${base}${pinned}`;
|
|
1304
|
+
}
|
|
1305
|
+
function processGateDecision(policy, stage, isAgent) {
|
|
1306
|
+
if (policy.processGate !== "block") {
|
|
1307
|
+
return {
|
|
1308
|
+
refuses: false,
|
|
1309
|
+
reason: `advisory: process gates report, they do not refuse \u2014 only sensors and other deterministic findings block. Set enforcement.posture="strict" (or processGate="block") to change that.`
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
if (stage === "pre-commit" || stage === "local") {
|
|
1313
|
+
return {
|
|
1314
|
+
refuses: false,
|
|
1315
|
+
reason: "advisory at commit time: process gates bind the sharing points (pre-push, CI), not local iteration."
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
if (stage !== "ci" && !isAgent && policy.humanCommits === "relaxed") {
|
|
1319
|
+
return {
|
|
1320
|
+
refuses: false,
|
|
1321
|
+
reason: `relaxed to a warning: no agent harness detected, so this human commit is not bound by agent process gates \u2014 set enforcement.humanCommits="strict" to change that.`
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
return { refuses: true, reason: "enforced: process gates bind at this sharing point." };
|
|
1325
|
+
}
|
|
1326
|
+
function penaltyOf(finding) {
|
|
1327
|
+
if (finding.severity === "error") return finding.impact ?? 25;
|
|
1328
|
+
if (finding.severity === "warn") return finding.impact ?? 8;
|
|
1329
|
+
return 0;
|
|
1330
|
+
}
|
|
1331
|
+
function computeBaselineHealth(findings, threshold) {
|
|
1332
|
+
const baseline = findings.filter((f) => !CONTENT_CODES.has(f.code));
|
|
1333
|
+
const penalty = baseline.reduce((sum, f) => sum + penaltyOf(f), 0);
|
|
1334
|
+
return {
|
|
1335
|
+
score: Math.max(0, Math.min(100, 100 - penalty)),
|
|
1336
|
+
threshold,
|
|
1337
|
+
checks: {
|
|
1338
|
+
total: findings.length,
|
|
1339
|
+
ok: findings.filter((f) => f.severity === "ok").length,
|
|
1340
|
+
warn: findings.filter((f) => f.severity === "warn").length,
|
|
1341
|
+
error: findings.filter((f) => f.severity === "error").length
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function dedupeRefusals(findings) {
|
|
1346
|
+
const byMemory = /* @__PURE__ */ new Map();
|
|
1347
|
+
const out = [];
|
|
1348
|
+
for (const finding of findings) {
|
|
1349
|
+
if (!CONTENT_CATCH_CODES.has(finding.code) || finding.severity !== "error") continue;
|
|
1350
|
+
const key = finding.memory_ids?.[0];
|
|
1351
|
+
if (!key) {
|
|
1352
|
+
out.push(finding);
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
const existing = byMemory.get(key);
|
|
1356
|
+
if (!existing || !existing.matched_line && finding.matched_line) byMemory.set(key, finding);
|
|
1357
|
+
}
|
|
1358
|
+
return [...byMemory.values(), ...out];
|
|
1359
|
+
}
|
|
1360
|
+
function decideVerdict(input) {
|
|
1361
|
+
const { policy, stage, isAgent } = input;
|
|
1362
|
+
const process2 = processGateDecision(policy, stage, isAgent);
|
|
1363
|
+
const findings = input.findings.map((finding) => {
|
|
1364
|
+
if (finding.severity !== "error" || !PROCESS_GATE_CODES.has(finding.code)) return finding;
|
|
1365
|
+
if (process2.refuses) return finding;
|
|
1366
|
+
return {
|
|
1367
|
+
...finding,
|
|
1368
|
+
severity: "warn",
|
|
1369
|
+
// Capped so a downgraded gate cannot dominate the health score it no longer refuses on.
|
|
1370
|
+
impact: Math.min(finding.impact ?? 8, 8),
|
|
1371
|
+
reason: process2.reason,
|
|
1372
|
+
message: `${finding.message} (${process2.reason})`
|
|
1373
|
+
};
|
|
1374
|
+
});
|
|
1375
|
+
const baselineHealth = computeBaselineHealth(findings, policy.scoreThreshold);
|
|
1376
|
+
const refusals = dedupeRefusals(findings);
|
|
1377
|
+
const hasErrors = findings.some((f) => f.severity === "error");
|
|
1378
|
+
return {
|
|
1379
|
+
findings,
|
|
1380
|
+
should_block: policy.mode === "strict" && hasErrors,
|
|
1381
|
+
actor: isAgent ? `agent (${(input.agentSignals ?? []).join(", ")})` : process2.refuses ? "human \u2014 strict (enforcement.humanCommits)" : "human \u2014 process gates relaxed",
|
|
1382
|
+
baseline_health: baselineHealth,
|
|
1383
|
+
refusals,
|
|
1384
|
+
process_gate_reason: process2.reason
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
function buildBaselineHealthFinding(findings, health, shouldBlock) {
|
|
1388
|
+
if (shouldBlock || health.score >= health.threshold) return null;
|
|
1389
|
+
const topPenalties = findings.filter((f) => !CONTENT_CODES.has(f.code)).map((f) => ({ code: f.code, penalty: penaltyOf(f) })).filter((p) => p.penalty > 0).sort((a, b) => b.penalty - a.penalty).slice(0, 3);
|
|
1390
|
+
return {
|
|
1391
|
+
severity: "warn",
|
|
1392
|
+
code: "enforcement-score-below-threshold",
|
|
1393
|
+
message: `Repo knowledge-layer health ${health.score}% is below the ${health.threshold}% target` + (topPenalties.length > 0 ? ` \u2014 top gaps: ${topPenalties.map((p) => `${p.code} (\u2212${p.penalty})`).join(", ")}` : "") + ". This measures the repo's baseline, not this change; it never blocks.",
|
|
1394
|
+
fix: "Fill the gaps above (bootstrap, briefing, recap), then rerun `hivelore enforce check`.",
|
|
1395
|
+
impact: 0
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1207
1399
|
// src/priority.ts
|
|
1208
1400
|
var DEFAULT_PRIORITY_SIGNALS = {
|
|
1209
1401
|
type: "",
|
|
@@ -1394,10 +1586,10 @@ function sensorPatternBrittleness(pattern) {
|
|
|
1394
1586
|
function normalizeProjectPath(value) {
|
|
1395
1587
|
return value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^[ab]\//, "").replace(/\/+$/g, "");
|
|
1396
1588
|
}
|
|
1397
|
-
function sensorAppliesToPath(sensor, anchorPaths,
|
|
1589
|
+
function sensorAppliesToPath(sensor, anchorPaths, path22) {
|
|
1398
1590
|
const scopes = sensor.paths.length > 0 ? sensor.paths : anchorPaths;
|
|
1399
1591
|
if (scopes.length === 0) return true;
|
|
1400
|
-
const target = normalizeProjectPath(
|
|
1592
|
+
const target = normalizeProjectPath(path22);
|
|
1401
1593
|
return scopes.some((rawScope) => {
|
|
1402
1594
|
const scope = normalizeProjectPath(rawScope);
|
|
1403
1595
|
if (!scope) return false;
|
|
@@ -1722,6 +1914,34 @@ function judgeProposedSensor(sensor, input) {
|
|
|
1722
1914
|
}
|
|
1723
1915
|
return { accepted: true, self_check, brittle };
|
|
1724
1916
|
}
|
|
1917
|
+
function explainSensorRejection(verdict, context) {
|
|
1918
|
+
const retry = context.style === "cli" ? "re-run" : "re-propose";
|
|
1919
|
+
switch (verdict.reason) {
|
|
1920
|
+
case "fires-on-current": {
|
|
1921
|
+
const where = verdict.self_check.fired_on.join(", ");
|
|
1922
|
+
const warnCommand = context.style === "cli" ? `hivelore sensors propose ${context.memoryId ?? "<memory-id>"} --pattern '<same>' --severity warn` : `propose_sensor({ memory_id: "${context.memoryId ?? "<memory-id>"}", pattern: "<same>", severity: "warn" })`;
|
|
1923
|
+
return [
|
|
1924
|
+
`A block sensor must be silent on the current code, and this one fires on: ${where}.`,
|
|
1925
|
+
"That means one of two things:",
|
|
1926
|
+
` 1. The faulty pattern is STILL PRESENT \u2014 the usual case when you document a problem before`,
|
|
1927
|
+
` fixing it. A block sensor cannot be armed yet. Arm it as a warning now, fix the code,`,
|
|
1928
|
+
` then promote it:`,
|
|
1929
|
+
` ${warnCommand}`,
|
|
1930
|
+
` \u2026then re-run the same proposal with severity "block" once ${where} is clean.`,
|
|
1931
|
+
` 2. The pattern also matches LEGITIMATE usage. Add or tighten the 'absent' companion so`,
|
|
1932
|
+
` correct usage is excluded, then ${retry}.`
|
|
1933
|
+
].join("\n");
|
|
1934
|
+
}
|
|
1935
|
+
case "fires-on-correct":
|
|
1936
|
+
return `Inverted: the pattern matches the lesson's OWN recommended fix (its \`Instead, use:\` approach) \u2014 it would block correct code and never the mistake. Point the pattern at the FAULTY usage, then ${retry}.`;
|
|
1937
|
+
case "missed-bad-example":
|
|
1938
|
+
return `The sensor did not match the bad example, so it won't catch the mistake. Adjust the pattern, then ${retry}.`;
|
|
1939
|
+
case "brittle":
|
|
1940
|
+
return `The pattern is brittle (${verdict.brittle}). Use a durable pattern (avoid hardcoded line numbers), then ${retry}.`;
|
|
1941
|
+
default:
|
|
1942
|
+
return `Re-propose with a discriminating pattern, then ${retry}.`;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1725
1945
|
function isHarnessErrorOutput(output) {
|
|
1726
1946
|
if (!output) return false;
|
|
1727
1947
|
return HARNESS_ERROR_SIGNATURES.some((re) => re.test(output));
|
|
@@ -1884,7 +2104,33 @@ function suggestSensorSeed(body, anchorPaths, options = {}) {
|
|
|
1884
2104
|
message: companion ? companionMessage(body, companion) : sensorMessageFromBody(body, token)
|
|
1885
2105
|
};
|
|
1886
2106
|
if (companion) seed.absent = escapeRegExp(companion.required);
|
|
1887
|
-
return seed;
|
|
2107
|
+
return seedFiresOnCorrectUsage(seed, body) ? null : seed;
|
|
2108
|
+
}
|
|
2109
|
+
function seedFiresOnCorrectUsage(seed, body) {
|
|
2110
|
+
const correct = correctUsageText(body);
|
|
2111
|
+
if (!correct) return false;
|
|
2112
|
+
let re;
|
|
2113
|
+
try {
|
|
2114
|
+
re = new RegExp(seed.pattern, "m");
|
|
2115
|
+
} catch {
|
|
2116
|
+
return true;
|
|
2117
|
+
}
|
|
2118
|
+
if (!re.test(correct)) return false;
|
|
2119
|
+
if (seed.absent) {
|
|
2120
|
+
try {
|
|
2121
|
+
if (new RegExp(seed.absent, "m").test(correct)) return false;
|
|
2122
|
+
} catch {
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
return true;
|
|
2126
|
+
}
|
|
2127
|
+
function correctUsageText(body) {
|
|
2128
|
+
const parts = [...extractCorrectApproachExamples(body)];
|
|
2129
|
+
const headingRe = /^#{1,6}\s+(?:how to apply|instead|correct|the fix|fix|recommended|do this|guidance)\b[^\n]*\n([\s\S]*?)(?=^#{1,6}\s|(?![\s\S]))/gim;
|
|
2130
|
+
for (const match of body.matchAll(headingRe)) {
|
|
2131
|
+
if (match[1]) parts.push(match[1]);
|
|
2132
|
+
}
|
|
2133
|
+
return parts.join("\n").trim();
|
|
1888
2134
|
}
|
|
1889
2135
|
function suggestSensorFromMemory(body, anchorPaths, options = {}) {
|
|
1890
2136
|
const seed = suggestSensorSeed(body, anchorPaths, options);
|
|
@@ -2649,10 +2895,11 @@ function allocateBudget(parts, maxTokens) {
|
|
|
2649
2895
|
}
|
|
2650
2896
|
|
|
2651
2897
|
// src/code-map.ts
|
|
2652
|
-
import { mkdir as
|
|
2653
|
-
import {
|
|
2898
|
+
import { mkdir as mkdir5, readFile as readFile7, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
|
|
2899
|
+
import { createHash as createHash2 } from "crypto";
|
|
2900
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2654
2901
|
import { spawnSync } from "child_process";
|
|
2655
|
-
import
|
|
2902
|
+
import path9 from "path";
|
|
2656
2903
|
|
|
2657
2904
|
// src/ast-parser.ts
|
|
2658
2905
|
import { fileURLToPath } from "url";
|
|
@@ -3064,27 +3311,53 @@ var CODE_MAP_DEFAULT_EXCLUDE = [
|
|
|
3064
3311
|
];
|
|
3065
3312
|
var TEST_FILE_RE = /\.(test|spec)\.[a-z]+$/i;
|
|
3066
3313
|
function codeMapPath(paths) {
|
|
3067
|
-
return
|
|
3314
|
+
return path9.join(paths.haiveDir, CODE_MAP_FILE);
|
|
3315
|
+
}
|
|
3316
|
+
function codeMapMetaPath(paths) {
|
|
3317
|
+
return path9.join(paths.runtimeDir, "code-map-meta.json");
|
|
3318
|
+
}
|
|
3319
|
+
function serializeCodeMap(map) {
|
|
3320
|
+
const files = {};
|
|
3321
|
+
for (const key of Object.keys(map.files).sort()) files[key] = map.files[key];
|
|
3322
|
+
return `${JSON.stringify({ version: map.version, files }, null, 2)}
|
|
3323
|
+
`;
|
|
3324
|
+
}
|
|
3325
|
+
function codeMapContentHash(map) {
|
|
3326
|
+
return createHash2("sha256").update(serializeCodeMap(map)).digest("hex").slice(0, 16);
|
|
3068
3327
|
}
|
|
3069
3328
|
async function loadCodeMap(paths) {
|
|
3070
3329
|
const file = codeMapPath(paths);
|
|
3071
|
-
if (!
|
|
3072
|
-
|
|
3330
|
+
if (!existsSync7(file)) return null;
|
|
3331
|
+
const parsed = JSON.parse(await readFile7(file, "utf8"));
|
|
3332
|
+
const generatedAt = parsed.generated_at ?? await readFile7(codeMapMetaPath(paths), "utf8").then((raw) => JSON.parse(raw).generated_at).catch(() => void 0) ?? await stat2(file).then((s) => s.mtime.toISOString()).catch(() => (/* @__PURE__ */ new Date(0)).toISOString());
|
|
3333
|
+
return { ...parsed, root: parsed.root ?? paths.root, generated_at: generatedAt };
|
|
3073
3334
|
}
|
|
3074
3335
|
async function saveCodeMap(paths, map) {
|
|
3075
3336
|
const file = codeMapPath(paths);
|
|
3076
|
-
|
|
3077
|
-
await
|
|
3337
|
+
const payload = serializeCodeMap(map);
|
|
3338
|
+
const current = existsSync7(file) ? await readFile7(file, "utf8").catch(() => null) : null;
|
|
3339
|
+
if (current === payload) return;
|
|
3340
|
+
await mkdir5(path9.dirname(file), { recursive: true });
|
|
3341
|
+
await writeFile4(file, payload, "utf8");
|
|
3342
|
+
await mkdir5(paths.runtimeDir, { recursive: true }).catch(() => {
|
|
3343
|
+
});
|
|
3344
|
+
await writeFile4(
|
|
3345
|
+
codeMapMetaPath(paths),
|
|
3346
|
+
`${JSON.stringify({ generated_at: (/* @__PURE__ */ new Date()).toISOString(), content_hash: codeMapContentHash(map) }, null, 2)}
|
|
3347
|
+
`,
|
|
3348
|
+
"utf8"
|
|
3349
|
+
).catch(() => {
|
|
3350
|
+
});
|
|
3078
3351
|
}
|
|
3079
3352
|
async function buildCodeMap(root, options = {}) {
|
|
3080
3353
|
const include = new Set(options.includeExtensions ?? CODE_MAP_DEFAULT_INCLUDE);
|
|
3081
3354
|
const exclude = new Set(options.excludeDirs ?? CODE_MAP_DEFAULT_EXCLUDE);
|
|
3082
3355
|
const files = {};
|
|
3083
3356
|
for await (const abs of collectSourceFiles(root, include, exclude, options.includeUntracked)) {
|
|
3084
|
-
const rel =
|
|
3357
|
+
const rel = path9.relative(root, abs).replace(/\\/g, "/");
|
|
3085
3358
|
if (rel.startsWith(".ai/")) continue;
|
|
3086
|
-
const content = await
|
|
3087
|
-
const ext =
|
|
3359
|
+
const content = await readFile7(abs, "utf8");
|
|
3360
|
+
const ext = path9.extname(abs).toLowerCase();
|
|
3088
3361
|
const entry = await parseFileEntry(content, ext);
|
|
3089
3362
|
if (entry.exports.length > 0) files[rel] = entry;
|
|
3090
3363
|
}
|
|
@@ -3105,11 +3378,11 @@ async function countSourceFilesOnDisk(root, options = {}) {
|
|
|
3105
3378
|
async function* collectSourceFiles(root, include, exclude, includeUntracked) {
|
|
3106
3379
|
const gitFiles = gitSourceFiles(root, include, exclude, includeUntracked === true);
|
|
3107
3380
|
if (gitFiles) {
|
|
3108
|
-
for (const rel of gitFiles) yield
|
|
3381
|
+
for (const rel of gitFiles) yield path9.join(root, rel);
|
|
3109
3382
|
for await (const nested of findNestedGitRepos(root, exclude)) {
|
|
3110
3383
|
const nestedFiles = gitSourceFiles(nested, include, exclude, includeUntracked === true);
|
|
3111
3384
|
if (nestedFiles) {
|
|
3112
|
-
for (const rel of nestedFiles) yield
|
|
3385
|
+
for (const rel of nestedFiles) yield path9.join(nested, rel);
|
|
3113
3386
|
}
|
|
3114
3387
|
}
|
|
3115
3388
|
return;
|
|
@@ -3128,8 +3401,8 @@ async function* findNestedGitRepos(root, exclude, depth = 0) {
|
|
|
3128
3401
|
if (!entry.isDirectory()) continue;
|
|
3129
3402
|
if (entry.name.startsWith(".")) continue;
|
|
3130
3403
|
if (exclude.has(entry.name)) continue;
|
|
3131
|
-
const full =
|
|
3132
|
-
if (
|
|
3404
|
+
const full = path9.join(root, entry.name);
|
|
3405
|
+
if (existsSync7(path9.join(full, ".git"))) {
|
|
3133
3406
|
yield full;
|
|
3134
3407
|
} else {
|
|
3135
3408
|
yield* findNestedGitRepos(full, exclude, depth + 1);
|
|
@@ -3158,11 +3431,11 @@ async function* walkSourceFiles(dir, include, exclude) {
|
|
|
3158
3431
|
if (entry.isDirectory()) continue;
|
|
3159
3432
|
}
|
|
3160
3433
|
if (exclude.has(entry.name)) continue;
|
|
3161
|
-
const full =
|
|
3434
|
+
const full = path9.join(dir, entry.name);
|
|
3162
3435
|
if (entry.isDirectory()) {
|
|
3163
3436
|
yield* walkSourceFiles(full, include, exclude);
|
|
3164
3437
|
} else if (entry.isFile()) {
|
|
3165
|
-
const ext =
|
|
3438
|
+
const ext = path9.extname(entry.name).toLowerCase();
|
|
3166
3439
|
if (include.has(ext) && !TEST_FILE_RE.test(entry.name)) yield full;
|
|
3167
3440
|
}
|
|
3168
3441
|
}
|
|
@@ -3173,7 +3446,7 @@ function isIncludedSourcePath(rel, include, exclude) {
|
|
|
3173
3446
|
const parts = normalized.split("/");
|
|
3174
3447
|
if (parts.some((part) => exclude.has(part))) return false;
|
|
3175
3448
|
const base = parts.at(-1) ?? "";
|
|
3176
|
-
const ext =
|
|
3449
|
+
const ext = path9.extname(base).toLowerCase();
|
|
3177
3450
|
return include.has(ext) && !TEST_FILE_RE.test(base);
|
|
3178
3451
|
}
|
|
3179
3452
|
var EXPORT_RE = /(?:^|;)[ \t]*export\s+(?:default\s+)?(async\s+)?(function|class|interface|type|const|let|var|enum)\s+(\*?)\s*([A-Za-z_$][\w$]*)/gm;
|
|
@@ -3435,10 +3708,10 @@ function queryCodeMap(map, options) {
|
|
|
3435
3708
|
}
|
|
3436
3709
|
|
|
3437
3710
|
// src/config.ts
|
|
3438
|
-
import { existsSync as
|
|
3711
|
+
import { existsSync as existsSync8 } from "fs";
|
|
3439
3712
|
import { readFileSync } from "fs";
|
|
3440
|
-
import { readFile as
|
|
3441
|
-
import
|
|
3713
|
+
import { readFile as readFile8, rm, writeFile as writeFile5 } from "fs/promises";
|
|
3714
|
+
import path10 from "path";
|
|
3442
3715
|
var CONFIG_FILE = "hivelore.config.json";
|
|
3443
3716
|
var LEGACY_CONFIG_FILE = "haive.config.json";
|
|
3444
3717
|
var DEFAULT_BRIEFING_EXCLUDE_TAGS = [
|
|
@@ -3534,19 +3807,19 @@ function antiPatternGateParams(gate) {
|
|
|
3534
3807
|
}
|
|
3535
3808
|
}
|
|
3536
3809
|
function configPath(paths) {
|
|
3537
|
-
return
|
|
3810
|
+
return path10.join(paths.haiveDir, CONFIG_FILE);
|
|
3538
3811
|
}
|
|
3539
3812
|
function resolveConfigPath(paths) {
|
|
3540
3813
|
const current = configPath(paths);
|
|
3541
|
-
if (
|
|
3542
|
-
const legacy =
|
|
3543
|
-
return
|
|
3814
|
+
if (existsSync8(current)) return current;
|
|
3815
|
+
const legacy = path10.join(paths.haiveDir, LEGACY_CONFIG_FILE);
|
|
3816
|
+
return existsSync8(legacy) ? legacy : current;
|
|
3544
3817
|
}
|
|
3545
3818
|
async function loadConfig(paths) {
|
|
3546
3819
|
const file = resolveConfigPath(paths);
|
|
3547
|
-
if (!
|
|
3820
|
+
if (!existsSync8(file)) return { ...DEFAULT_CONFIG };
|
|
3548
3821
|
try {
|
|
3549
|
-
const raw = await
|
|
3822
|
+
const raw = await readFile8(file, "utf8");
|
|
3550
3823
|
const parsed = JSON.parse(raw);
|
|
3551
3824
|
const merged = mergeConfig(DEFAULT_CONFIG, parsed);
|
|
3552
3825
|
if (merged.autopilot) {
|
|
@@ -3559,7 +3832,7 @@ async function loadConfig(paths) {
|
|
|
3559
3832
|
}
|
|
3560
3833
|
function loadConfigSync(paths) {
|
|
3561
3834
|
const file = resolveConfigPath(paths);
|
|
3562
|
-
if (!
|
|
3835
|
+
if (!existsSync8(file)) return { ...DEFAULT_CONFIG };
|
|
3563
3836
|
try {
|
|
3564
3837
|
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
3565
3838
|
const merged = mergeConfig(DEFAULT_CONFIG, parsed);
|
|
@@ -3569,9 +3842,9 @@ function loadConfigSync(paths) {
|
|
|
3569
3842
|
}
|
|
3570
3843
|
}
|
|
3571
3844
|
async function saveConfig(paths, config) {
|
|
3572
|
-
await
|
|
3573
|
-
const legacy =
|
|
3574
|
-
if (
|
|
3845
|
+
await writeFile5(configPath(paths), JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
3846
|
+
const legacy = path10.join(paths.haiveDir, LEGACY_CONFIG_FILE);
|
|
3847
|
+
if (existsSync8(legacy)) {
|
|
3575
3848
|
try {
|
|
3576
3849
|
await rm(legacy, { force: true });
|
|
3577
3850
|
} catch {
|
|
@@ -3594,21 +3867,21 @@ function mergeConfig(base, override) {
|
|
|
3594
3867
|
}
|
|
3595
3868
|
|
|
3596
3869
|
// src/cross-repo.ts
|
|
3597
|
-
import { existsSync as
|
|
3598
|
-
import { mkdir as
|
|
3599
|
-
import
|
|
3870
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3871
|
+
import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
|
|
3872
|
+
import path11 from "path";
|
|
3600
3873
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3601
3874
|
async function loadImportMap(cacheDir) {
|
|
3602
|
-
const mapPath =
|
|
3603
|
-
if (!
|
|
3875
|
+
const mapPath = path11.join(cacheDir, "import-map.json");
|
|
3876
|
+
if (!existsSync9(mapPath)) return {};
|
|
3604
3877
|
try {
|
|
3605
|
-
return JSON.parse(await
|
|
3878
|
+
return JSON.parse(await readFile9(mapPath, "utf8"));
|
|
3606
3879
|
} catch {
|
|
3607
3880
|
return {};
|
|
3608
3881
|
}
|
|
3609
3882
|
}
|
|
3610
3883
|
async function saveImportMap(cacheDir, map) {
|
|
3611
|
-
await
|
|
3884
|
+
await writeFile6(path11.join(cacheDir, "import-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
3612
3885
|
}
|
|
3613
3886
|
async function pullCrossRepoSources(paths, config, projectRoot) {
|
|
3614
3887
|
const sources = config.crossRepoSources ?? [];
|
|
@@ -3629,8 +3902,8 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3629
3902
|
};
|
|
3630
3903
|
let sourceRoot = null;
|
|
3631
3904
|
if (source.path) {
|
|
3632
|
-
const resolved =
|
|
3633
|
-
if (!
|
|
3905
|
+
const resolved = path11.resolve(projectRoot, source.path);
|
|
3906
|
+
if (!existsSync9(resolved)) {
|
|
3634
3907
|
report.errors.push(`Path not found: ${resolved}`);
|
|
3635
3908
|
return report;
|
|
3636
3909
|
}
|
|
@@ -3643,7 +3916,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3643
3916
|
return report;
|
|
3644
3917
|
}
|
|
3645
3918
|
const sourcePaths = resolveHaivePaths(sourceRoot);
|
|
3646
|
-
if (!
|
|
3919
|
+
if (!existsSync9(sourcePaths.memoriesDir)) {
|
|
3647
3920
|
report.errors.push(`No .ai/memories/ found at ${sourceRoot}`);
|
|
3648
3921
|
return report;
|
|
3649
3922
|
}
|
|
@@ -3666,10 +3939,10 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3666
3939
|
report.skipped.push("no shared memories found in source");
|
|
3667
3940
|
return report;
|
|
3668
3941
|
}
|
|
3669
|
-
const destDir =
|
|
3670
|
-
await
|
|
3671
|
-
const cacheDir =
|
|
3672
|
-
await
|
|
3942
|
+
const destDir = path11.join(paths.memoriesDir, "shared", source.name);
|
|
3943
|
+
await mkdir6(destDir, { recursive: true });
|
|
3944
|
+
const cacheDir = path11.join(paths.haiveDir, ".cache", "cross-repo", source.name);
|
|
3945
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
3673
3946
|
const importMap = await loadImportMap(cacheDir);
|
|
3674
3947
|
const mapDirty = false;
|
|
3675
3948
|
let dirty = mapDirty;
|
|
@@ -3683,7 +3956,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3683
3956
|
|
|
3684
3957
|
`;
|
|
3685
3958
|
const existingLocalPath = importMap[sourceId];
|
|
3686
|
-
if (existingLocalPath &&
|
|
3959
|
+
if (existingLocalPath && existsSync9(existingLocalPath)) {
|
|
3687
3960
|
const existingFiles = await loadMemoriesFromDir(destDir);
|
|
3688
3961
|
const existingEntry = existingFiles.find(({ filePath }) => filePath === existingLocalPath);
|
|
3689
3962
|
const sourceBodyStripped = memory.body.trim();
|
|
@@ -3694,7 +3967,7 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3694
3967
|
}
|
|
3695
3968
|
const updatedBody = importedBodyPrefix + memory.body;
|
|
3696
3969
|
if (existingEntry) {
|
|
3697
|
-
await
|
|
3970
|
+
await writeFile6(
|
|
3698
3971
|
existingLocalPath,
|
|
3699
3972
|
serializeMemory({ frontmatter: existingEntry.memory.frontmatter, body: updatedBody }),
|
|
3700
3973
|
"utf8"
|
|
@@ -3718,8 +3991,8 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3718
3991
|
topic: fm.topic ? `${source.name}:${fm.topic}` : void 0
|
|
3719
3992
|
});
|
|
3720
3993
|
const body = importedBodyPrefix + memory.body;
|
|
3721
|
-
const destPath =
|
|
3722
|
-
await
|
|
3994
|
+
const destPath = path11.join(destDir, `${newFm.id}.md`);
|
|
3995
|
+
await writeFile6(destPath, serializeMemory({ frontmatter: newFm, body }), "utf8");
|
|
3723
3996
|
importMap[sourceId] = destPath;
|
|
3724
3997
|
dirty = true;
|
|
3725
3998
|
report.imported.push(sourceId);
|
|
@@ -3729,9 +4002,9 @@ async function pullFromSource(paths, source, projectRoot) {
|
|
|
3729
4002
|
return report;
|
|
3730
4003
|
}
|
|
3731
4004
|
async function cloneOrFetchGitSource(source, paths, report) {
|
|
3732
|
-
const cacheDir =
|
|
3733
|
-
await
|
|
3734
|
-
if (
|
|
4005
|
+
const cacheDir = path11.join(paths.haiveDir, ".cache", "cross-repo", source.name);
|
|
4006
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
4007
|
+
if (existsSync9(path11.join(cacheDir, ".git"))) {
|
|
3735
4008
|
const result = spawnSync2("git", ["fetch", "--depth=1", "origin"], {
|
|
3736
4009
|
cwd: cacheDir,
|
|
3737
4010
|
encoding: "utf8"
|
|
@@ -3756,9 +4029,9 @@ async function cloneOrFetchGitSource(source, paths, report) {
|
|
|
3756
4029
|
}
|
|
3757
4030
|
|
|
3758
4031
|
// src/dep-tracker.ts
|
|
3759
|
-
import { existsSync as
|
|
3760
|
-
import { readFile as
|
|
3761
|
-
import
|
|
4032
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4033
|
+
import { readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
4034
|
+
import path12 from "path";
|
|
3762
4035
|
function parsePackageJson(content) {
|
|
3763
4036
|
try {
|
|
3764
4037
|
const pkg = JSON.parse(content);
|
|
@@ -3830,7 +4103,7 @@ var KNOWN_MANIFESTS = [
|
|
|
3830
4103
|
{ name: "pom.xml", parser: parsePomXml }
|
|
3831
4104
|
];
|
|
3832
4105
|
function getParser(file) {
|
|
3833
|
-
const base =
|
|
4106
|
+
const base = path12.basename(file);
|
|
3834
4107
|
return KNOWN_MANIFESTS.find((m) => m.name === base)?.parser ?? null;
|
|
3835
4108
|
}
|
|
3836
4109
|
function extractMajor(version) {
|
|
@@ -3848,32 +4121,32 @@ function isMajorBump(from, to) {
|
|
|
3848
4121
|
}
|
|
3849
4122
|
function resolveManifestFiles(projectRoot, configuredFiles) {
|
|
3850
4123
|
if (configuredFiles !== void 0) {
|
|
3851
|
-
return configuredFiles.map((f) =>
|
|
4124
|
+
return configuredFiles.map((f) => path12.resolve(projectRoot, f)).filter(existsSync10);
|
|
3852
4125
|
}
|
|
3853
|
-
return KNOWN_MANIFESTS.map(({ name }) =>
|
|
4126
|
+
return KNOWN_MANIFESTS.map(({ name }) => path12.join(projectRoot, name)).filter(existsSync10);
|
|
3854
4127
|
}
|
|
3855
4128
|
async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
|
|
3856
|
-
const contractsDir =
|
|
3857
|
-
await
|
|
4129
|
+
const contractsDir = path12.join(haiveDir, "contracts");
|
|
4130
|
+
await mkdir7(contractsDir, { recursive: true });
|
|
3858
4131
|
const results = [];
|
|
3859
4132
|
for (const manifestPath of manifestFiles) {
|
|
3860
4133
|
const parser2 = getParser(manifestPath);
|
|
3861
4134
|
if (!parser2) continue;
|
|
3862
|
-
const content = await
|
|
4135
|
+
const content = await readFile10(manifestPath, "utf8");
|
|
3863
4136
|
const currentDeps = parser2(content);
|
|
3864
|
-
const lockName = `deps-${
|
|
3865
|
-
const lockPath =
|
|
3866
|
-
if (!
|
|
4137
|
+
const lockName = `deps-${path12.basename(manifestPath)}.lock`;
|
|
4138
|
+
const lockPath = path12.join(contractsDir, lockName);
|
|
4139
|
+
if (!existsSync10(lockPath)) {
|
|
3867
4140
|
const snapshot2 = {
|
|
3868
|
-
file:
|
|
3869
|
-
format:
|
|
4141
|
+
file: path12.relative(projectRoot, manifestPath),
|
|
4142
|
+
format: path12.basename(manifestPath),
|
|
3870
4143
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3871
4144
|
deps: currentDeps
|
|
3872
4145
|
};
|
|
3873
|
-
await
|
|
4146
|
+
await writeFile7(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
|
|
3874
4147
|
continue;
|
|
3875
4148
|
}
|
|
3876
|
-
const snapshot = JSON.parse(await
|
|
4149
|
+
const snapshot = JSON.parse(await readFile10(lockPath, "utf8"));
|
|
3877
4150
|
const changes = [];
|
|
3878
4151
|
for (const [name, currentVer] of Object.entries(currentDeps)) {
|
|
3879
4152
|
const prevVer = snapshot.deps[name];
|
|
@@ -3887,22 +4160,22 @@ async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
|
|
|
3887
4160
|
}
|
|
3888
4161
|
}
|
|
3889
4162
|
if (changes.length > 0) {
|
|
3890
|
-
results.push({ file:
|
|
4163
|
+
results.push({ file: path12.relative(projectRoot, manifestPath), changes });
|
|
3891
4164
|
const updated = {
|
|
3892
4165
|
...snapshot,
|
|
3893
4166
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3894
4167
|
deps: currentDeps
|
|
3895
4168
|
};
|
|
3896
|
-
await
|
|
4169
|
+
await writeFile7(lockPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
3897
4170
|
}
|
|
3898
4171
|
}
|
|
3899
4172
|
return results;
|
|
3900
4173
|
}
|
|
3901
4174
|
|
|
3902
4175
|
// src/contract-watcher.ts
|
|
3903
|
-
import { existsSync as
|
|
3904
|
-
import { readFile as
|
|
3905
|
-
import
|
|
4176
|
+
import { existsSync as existsSync11 } from "fs";
|
|
4177
|
+
import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
4178
|
+
import path13 from "path";
|
|
3906
4179
|
import crypto from "crypto";
|
|
3907
4180
|
function sha256(content) {
|
|
3908
4181
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
@@ -4105,14 +4378,14 @@ function diffSnapshots(before, after) {
|
|
|
4105
4378
|
return changes;
|
|
4106
4379
|
}
|
|
4107
4380
|
function contractLockPath(haiveDir, name) {
|
|
4108
|
-
return
|
|
4381
|
+
return path13.join(haiveDir, "contracts", `${name}.lock`);
|
|
4109
4382
|
}
|
|
4110
4383
|
async function snapshotContract(projectRoot, haiveDir, contract) {
|
|
4111
|
-
const filePath =
|
|
4112
|
-
if (!
|
|
4384
|
+
const filePath = path13.resolve(projectRoot, contract.path);
|
|
4385
|
+
if (!existsSync11(filePath)) {
|
|
4113
4386
|
throw new Error(`Contract file not found: ${filePath}`);
|
|
4114
4387
|
}
|
|
4115
|
-
const content = await
|
|
4388
|
+
const content = await readFile11(filePath, "utf8");
|
|
4116
4389
|
const parsed = parseByFormat(content, contract.format, filePath);
|
|
4117
4390
|
const snapshot = {
|
|
4118
4391
|
name: contract.name,
|
|
@@ -4122,23 +4395,23 @@ async function snapshotContract(projectRoot, haiveDir, contract) {
|
|
|
4122
4395
|
hash: sha256(content),
|
|
4123
4396
|
...parsed
|
|
4124
4397
|
};
|
|
4125
|
-
const contractsDir =
|
|
4126
|
-
await
|
|
4127
|
-
await
|
|
4398
|
+
const contractsDir = path13.join(haiveDir, "contracts");
|
|
4399
|
+
await mkdir8(contractsDir, { recursive: true });
|
|
4400
|
+
await writeFile8(contractLockPath(haiveDir, contract.name), JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
4128
4401
|
return snapshot;
|
|
4129
4402
|
}
|
|
4130
4403
|
async function diffContract(projectRoot, haiveDir, contract) {
|
|
4131
|
-
const filePath =
|
|
4132
|
-
if (!
|
|
4404
|
+
const filePath = path13.resolve(projectRoot, contract.path);
|
|
4405
|
+
if (!existsSync11(filePath)) {
|
|
4133
4406
|
return { contract: contract.name, file: contract.path, changes: [], unchanged: true };
|
|
4134
4407
|
}
|
|
4135
4408
|
const lockPath = contractLockPath(haiveDir, contract.name);
|
|
4136
|
-
if (!
|
|
4409
|
+
if (!existsSync11(lockPath)) {
|
|
4137
4410
|
await snapshotContract(projectRoot, haiveDir, contract);
|
|
4138
4411
|
return { contract: contract.name, file: contract.path, changes: [], unchanged: true };
|
|
4139
4412
|
}
|
|
4140
|
-
const content = await
|
|
4141
|
-
const beforeSnapshot = JSON.parse(await
|
|
4413
|
+
const content = await readFile11(filePath, "utf8");
|
|
4414
|
+
const beforeSnapshot = JSON.parse(await readFile11(lockPath, "utf8"));
|
|
4142
4415
|
const afterParsed = parseByFormat(content, contract.format, filePath);
|
|
4143
4416
|
const afterSnapshot = {
|
|
4144
4417
|
...beforeSnapshot,
|
|
@@ -4148,7 +4421,7 @@ async function diffContract(projectRoot, haiveDir, contract) {
|
|
|
4148
4421
|
};
|
|
4149
4422
|
const changes = diffSnapshots(beforeSnapshot, afterSnapshot);
|
|
4150
4423
|
if (changes.length > 0) {
|
|
4151
|
-
await
|
|
4424
|
+
await writeFile8(lockPath, JSON.stringify(afterSnapshot, null, 2) + "\n", "utf8");
|
|
4152
4425
|
}
|
|
4153
4426
|
return {
|
|
4154
4427
|
contract: contract.name,
|
|
@@ -4166,27 +4439,27 @@ async function watchContracts(projectRoot, haiveDir, contractFiles) {
|
|
|
4166
4439
|
}
|
|
4167
4440
|
|
|
4168
4441
|
// src/usage-log.ts
|
|
4169
|
-
import { appendFile as appendFile2, mkdir as
|
|
4170
|
-
import { existsSync as
|
|
4171
|
-
import
|
|
4442
|
+
import { appendFile as appendFile2, mkdir as mkdir9, readFile as readFile12, stat as stat3 } from "fs/promises";
|
|
4443
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4444
|
+
import path14 from "path";
|
|
4172
4445
|
var USAGE_LOG_FILE = "tool-usage.jsonl";
|
|
4173
4446
|
var USAGE_LOG_DIR = ".usage";
|
|
4174
4447
|
function usageLogPath(paths) {
|
|
4175
|
-
return
|
|
4448
|
+
return path14.join(paths.haiveDir, USAGE_LOG_DIR, USAGE_LOG_FILE);
|
|
4176
4449
|
}
|
|
4177
4450
|
async function appendUsageEvent(paths, event) {
|
|
4178
4451
|
try {
|
|
4179
4452
|
const file = usageLogPath(paths);
|
|
4180
|
-
const dir =
|
|
4181
|
-
if (!
|
|
4453
|
+
const dir = path14.dirname(file);
|
|
4454
|
+
if (!existsSync12(dir)) await mkdir9(dir, { recursive: true });
|
|
4182
4455
|
await appendFile2(file, JSON.stringify(event) + "\n", "utf8");
|
|
4183
4456
|
} catch {
|
|
4184
4457
|
}
|
|
4185
4458
|
}
|
|
4186
4459
|
async function readUsageEvents(paths) {
|
|
4187
4460
|
const file = usageLogPath(paths);
|
|
4188
|
-
if (!
|
|
4189
|
-
const raw = await
|
|
4461
|
+
if (!existsSync12(file)) return [];
|
|
4462
|
+
const raw = await readFile12(file, "utf8");
|
|
4190
4463
|
const out = [];
|
|
4191
4464
|
for (const line of raw.split("\n")) {
|
|
4192
4465
|
if (!line) continue;
|
|
@@ -4235,25 +4508,25 @@ function parseSince(input) {
|
|
|
4235
4508
|
}
|
|
4236
4509
|
async function usageLogSize(paths) {
|
|
4237
4510
|
const file = usageLogPath(paths);
|
|
4238
|
-
if (!
|
|
4239
|
-
const st = await
|
|
4240
|
-
const raw = await
|
|
4511
|
+
if (!existsSync12(file)) return { exists: false, size_bytes: 0, lines: 0 };
|
|
4512
|
+
const st = await stat3(file);
|
|
4513
|
+
const raw = await readFile12(file, "utf8");
|
|
4241
4514
|
return { exists: true, size_bytes: st.size, lines: raw.split("\n").filter((l) => l).length };
|
|
4242
4515
|
}
|
|
4243
4516
|
|
|
4244
4517
|
// src/friction.ts
|
|
4245
|
-
import { appendFile as appendFile3, mkdir as
|
|
4246
|
-
import { existsSync as
|
|
4247
|
-
import { createHash as
|
|
4248
|
-
import
|
|
4518
|
+
import { appendFile as appendFile3, mkdir as mkdir10, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
|
|
4519
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4520
|
+
import { createHash as createHash3 } from "crypto";
|
|
4521
|
+
import path15 from "path";
|
|
4249
4522
|
var FRICTION_LOG_FILE = "friction.jsonl";
|
|
4250
4523
|
var FRICTION_STATE_FILE = "friction-state.json";
|
|
4251
4524
|
var FRICTION_FIELD_MAX = 2e3;
|
|
4252
4525
|
function frictionLogPath(paths) {
|
|
4253
|
-
return
|
|
4526
|
+
return path15.join(paths.runtimeDir, FRICTION_LOG_FILE);
|
|
4254
4527
|
}
|
|
4255
4528
|
function frictionStatePath(paths) {
|
|
4256
|
-
return
|
|
4529
|
+
return path15.join(paths.runtimeDir, FRICTION_STATE_FILE);
|
|
4257
4530
|
}
|
|
4258
4531
|
function normalizeFrictionSummary(value) {
|
|
4259
4532
|
return value.toLowerCase().replace(/[a-z]?:?[\\/](?:[\w.-]+[\\/])+/g, "/").replace(/\d{3,}/g, "N").replace(/\s+/g, " ").trim();
|
|
@@ -4264,7 +4537,7 @@ function frictionFingerprint(input) {
|
|
|
4264
4537
|
input.surface.trim().toLowerCase(),
|
|
4265
4538
|
normalizeFrictionSummary(input.summary)
|
|
4266
4539
|
].join("|");
|
|
4267
|
-
return
|
|
4540
|
+
return createHash3("sha256").update(basis).digest("hex").slice(0, 16);
|
|
4268
4541
|
}
|
|
4269
4542
|
function truncate(value) {
|
|
4270
4543
|
if (value === void 0) return void 0;
|
|
@@ -4298,7 +4571,7 @@ async function appendFrictionReport(paths, input) {
|
|
|
4298
4571
|
};
|
|
4299
4572
|
const prior = (await readFrictionReports(paths)).filter((r) => r.fingerprint === fingerprint);
|
|
4300
4573
|
try {
|
|
4301
|
-
if (!
|
|
4574
|
+
if (!existsSync13(paths.runtimeDir)) await mkdir10(paths.runtimeDir, { recursive: true });
|
|
4302
4575
|
await appendFile3(frictionLogPath(paths), JSON.stringify(report) + "\n", "utf8");
|
|
4303
4576
|
} catch {
|
|
4304
4577
|
}
|
|
@@ -4312,10 +4585,10 @@ async function appendFrictionReport(paths, input) {
|
|
|
4312
4585
|
}
|
|
4313
4586
|
async function readFrictionReports(paths) {
|
|
4314
4587
|
const file = frictionLogPath(paths);
|
|
4315
|
-
if (!
|
|
4588
|
+
if (!existsSync13(file)) return [];
|
|
4316
4589
|
let raw;
|
|
4317
4590
|
try {
|
|
4318
|
-
raw = await
|
|
4591
|
+
raw = await readFile13(file, "utf8");
|
|
4319
4592
|
} catch {
|
|
4320
4593
|
return [];
|
|
4321
4594
|
}
|
|
@@ -4332,17 +4605,17 @@ async function readFrictionReports(paths) {
|
|
|
4332
4605
|
}
|
|
4333
4606
|
async function loadFrictionState(paths) {
|
|
4334
4607
|
const file = frictionStatePath(paths);
|
|
4335
|
-
if (!
|
|
4608
|
+
if (!existsSync13(file)) return {};
|
|
4336
4609
|
try {
|
|
4337
|
-
const parsed = JSON.parse(await
|
|
4610
|
+
const parsed = JSON.parse(await readFile13(file, "utf8"));
|
|
4338
4611
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
4339
4612
|
} catch {
|
|
4340
4613
|
return {};
|
|
4341
4614
|
}
|
|
4342
4615
|
}
|
|
4343
4616
|
async function saveFrictionState(paths, state) {
|
|
4344
|
-
if (!
|
|
4345
|
-
await
|
|
4617
|
+
if (!existsSync13(paths.runtimeDir)) await mkdir10(paths.runtimeDir, { recursive: true });
|
|
4618
|
+
await writeFile9(frictionStatePath(paths), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
4346
4619
|
}
|
|
4347
4620
|
async function setFrictionStatus(paths, fingerprint, status, url) {
|
|
4348
4621
|
const state = await loadFrictionState(paths);
|
|
@@ -4479,21 +4752,21 @@ function extractActionsBriefBody(markdown, maxChars = MAX_DEFAULT_CHARS) {
|
|
|
4479
4752
|
}
|
|
4480
4753
|
|
|
4481
4754
|
// src/resolve-project.ts
|
|
4482
|
-
import { existsSync as
|
|
4483
|
-
import
|
|
4755
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4756
|
+
import path16 from "path";
|
|
4484
4757
|
var ROOT_MARKERS2 = [".ai", ".git", "package.json"];
|
|
4485
4758
|
function markersAtRoot(root) {
|
|
4486
4759
|
const found = [];
|
|
4487
4760
|
for (const m of ROOT_MARKERS2) {
|
|
4488
|
-
if (
|
|
4761
|
+
if (existsSync14(path16.join(root, m))) found.push(m);
|
|
4489
4762
|
}
|
|
4490
4763
|
return found;
|
|
4491
4764
|
}
|
|
4492
4765
|
function resolveProjectInfo(opts = {}) {
|
|
4493
4766
|
const env = opts.env ?? process.env;
|
|
4494
|
-
const cwd =
|
|
4767
|
+
const cwd = path16.resolve(opts.cwd ?? process.cwd());
|
|
4495
4768
|
const raw = env.HAIVE_PROJECT_ROOT;
|
|
4496
|
-
const explicit = raw !== void 0 && raw !== "" ?
|
|
4769
|
+
const explicit = raw !== void 0 && raw !== "" ? path16.resolve(raw) : null;
|
|
4497
4770
|
const resolvedRoot = explicit ?? findProjectRoot(cwd);
|
|
4498
4771
|
const paths = resolveHaivePaths(resolvedRoot);
|
|
4499
4772
|
return {
|
|
@@ -4501,8 +4774,8 @@ function resolveProjectInfo(opts = {}) {
|
|
|
4501
4774
|
resolved_root: resolvedRoot,
|
|
4502
4775
|
haive_project_root_env: explicit,
|
|
4503
4776
|
explicit_root: explicit != null,
|
|
4504
|
-
haive_dir_exists:
|
|
4505
|
-
memories_dir_exists:
|
|
4777
|
+
haive_dir_exists: existsSync14(paths.haiveDir),
|
|
4778
|
+
memories_dir_exists: existsSync14(paths.memoriesDir),
|
|
4506
4779
|
runtime_dir: paths.runtimeDir,
|
|
4507
4780
|
markers_found: markersAtRoot(resolvedRoot)
|
|
4508
4781
|
};
|
|
@@ -4757,16 +5030,16 @@ function findLexicalConflictPairs(memories, opts) {
|
|
|
4757
5030
|
}
|
|
4758
5031
|
|
|
4759
5032
|
// src/runtime-journal.ts
|
|
4760
|
-
import { mkdir as
|
|
4761
|
-
import { existsSync as
|
|
4762
|
-
import
|
|
5033
|
+
import { mkdir as mkdir11, readFile as readFile14, appendFile as appendFile4 } from "fs/promises";
|
|
5034
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5035
|
+
import path17 from "path";
|
|
4763
5036
|
var RUNTIME_JOURNAL_FILENAME = "session-journal.ndjson";
|
|
4764
5037
|
function runtimeJournalPath(paths) {
|
|
4765
|
-
return
|
|
5038
|
+
return path17.join(paths.runtimeDir, RUNTIME_JOURNAL_FILENAME);
|
|
4766
5039
|
}
|
|
4767
5040
|
async function appendRuntimeJournalEntry(paths, entry) {
|
|
4768
5041
|
try {
|
|
4769
|
-
await
|
|
5042
|
+
await mkdir11(paths.runtimeDir, { recursive: true });
|
|
4770
5043
|
const line = {
|
|
4771
5044
|
ts: entry.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4772
5045
|
kind: entry.kind,
|
|
@@ -4784,9 +5057,9 @@ async function appendRuntimeJournalEntry(paths, entry) {
|
|
|
4784
5057
|
}
|
|
4785
5058
|
async function readRuntimeJournalTail(paths, limit) {
|
|
4786
5059
|
const file = runtimeJournalPath(paths);
|
|
4787
|
-
if (!
|
|
5060
|
+
if (!existsSync15(file) || limit <= 0) return [];
|
|
4788
5061
|
try {
|
|
4789
|
-
const raw = await
|
|
5062
|
+
const raw = await readFile14(file, "utf8");
|
|
4790
5063
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
4791
5064
|
const parsed = [];
|
|
4792
5065
|
for (const line of lines.slice(-limit)) {
|
|
@@ -4802,22 +5075,22 @@ async function readRuntimeJournalTail(paths, limit) {
|
|
|
4802
5075
|
}
|
|
4803
5076
|
|
|
4804
5077
|
// src/enforcement.ts
|
|
4805
|
-
import { mkdir as
|
|
4806
|
-
import { existsSync as
|
|
4807
|
-
import
|
|
5078
|
+
import { mkdir as mkdir12, readdir as readdir4, readFile as readFile15, writeFile as writeFile10 } from "fs/promises";
|
|
5079
|
+
import { existsSync as existsSync16 } from "fs";
|
|
5080
|
+
import path18 from "path";
|
|
4808
5081
|
var BRIEFING_MARKER_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
4809
5082
|
var SESSION_RECAP_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
4810
5083
|
function enforcementDir(paths) {
|
|
4811
|
-
return
|
|
5084
|
+
return path18.join(paths.runtimeDir, "enforcement");
|
|
4812
5085
|
}
|
|
4813
5086
|
function briefingMarkersDir(paths) {
|
|
4814
|
-
return
|
|
5087
|
+
return path18.join(enforcementDir(paths), "briefings");
|
|
4815
5088
|
}
|
|
4816
5089
|
function normalizeSessionId(sessionId) {
|
|
4817
5090
|
return (sessionId?.trim() || "default").replace(/[^a-zA-Z0-9_.-]+/g, "-").slice(0, 120);
|
|
4818
5091
|
}
|
|
4819
5092
|
function briefingMarkerPath(paths, sessionId) {
|
|
4820
|
-
return
|
|
5093
|
+
return path18.join(briefingMarkersDir(paths), `${normalizeSessionId(sessionId)}.json`);
|
|
4821
5094
|
}
|
|
4822
5095
|
async function writeBriefingMarker(paths, input) {
|
|
4823
5096
|
const sessionId = normalizeSessionId(input.sessionId);
|
|
@@ -4842,8 +5115,8 @@ async function writeBriefingMarker(paths, input) {
|
|
|
4842
5115
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4843
5116
|
root: paths.root
|
|
4844
5117
|
};
|
|
4845
|
-
await
|
|
4846
|
-
await
|
|
5118
|
+
await mkdir12(briefingMarkersDir(paths), { recursive: true });
|
|
5119
|
+
await writeFile10(
|
|
4847
5120
|
briefingMarkerPath(paths, marker.session_id),
|
|
4848
5121
|
JSON.stringify(marker, null, 2) + "\n",
|
|
4849
5122
|
"utf8"
|
|
@@ -4852,9 +5125,9 @@ async function writeBriefingMarker(paths, input) {
|
|
|
4852
5125
|
}
|
|
4853
5126
|
async function readSessionBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKER_TTL_MS) {
|
|
4854
5127
|
const file = briefingMarkerPath(paths, sessionId);
|
|
4855
|
-
if (!
|
|
5128
|
+
if (!existsSync16(file)) return null;
|
|
4856
5129
|
try {
|
|
4857
|
-
const marker = JSON.parse(await
|
|
5130
|
+
const marker = JSON.parse(await readFile15(file, "utf8"));
|
|
4858
5131
|
const created = Date.parse(marker.created_at);
|
|
4859
5132
|
if (!Number.isFinite(created) || Date.now() - created > ttlMs) return null;
|
|
4860
5133
|
return marker;
|
|
@@ -4866,18 +5139,18 @@ async function hasRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKER
|
|
|
4866
5139
|
const now = Date.now();
|
|
4867
5140
|
const candidates = [];
|
|
4868
5141
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
4869
|
-
if (
|
|
5142
|
+
if (existsSync16(exact)) candidates.push(exact);
|
|
4870
5143
|
try {
|
|
4871
5144
|
const dir = briefingMarkersDir(paths);
|
|
4872
5145
|
const files = await readdir4(dir);
|
|
4873
5146
|
for (const file of files) {
|
|
4874
|
-
if (file.endsWith(".json")) candidates.push(
|
|
5147
|
+
if (file.endsWith(".json")) candidates.push(path18.join(dir, file));
|
|
4875
5148
|
}
|
|
4876
5149
|
} catch {
|
|
4877
5150
|
}
|
|
4878
5151
|
for (const file of new Set(candidates)) {
|
|
4879
5152
|
try {
|
|
4880
|
-
const marker = JSON.parse(await
|
|
5153
|
+
const marker = JSON.parse(await readFile15(file, "utf8"));
|
|
4881
5154
|
const created = Date.parse(marker.created_at);
|
|
4882
5155
|
if (Number.isFinite(created) && now - created <= ttlMs) return true;
|
|
4883
5156
|
} catch {
|
|
@@ -4889,12 +5162,12 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
4889
5162
|
const now = Date.now();
|
|
4890
5163
|
const candidates = [];
|
|
4891
5164
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
4892
|
-
if (
|
|
5165
|
+
if (existsSync16(exact)) candidates.push(exact);
|
|
4893
5166
|
try {
|
|
4894
5167
|
const dir = briefingMarkersDir(paths);
|
|
4895
5168
|
const files = await readdir4(dir);
|
|
4896
5169
|
for (const file of files) {
|
|
4897
|
-
if (file.endsWith(".json")) candidates.push(
|
|
5170
|
+
if (file.endsWith(".json")) candidates.push(path18.join(dir, file));
|
|
4898
5171
|
}
|
|
4899
5172
|
} catch {
|
|
4900
5173
|
}
|
|
@@ -4902,7 +5175,7 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
4902
5175
|
let freshestTs = 0;
|
|
4903
5176
|
for (const file of new Set(candidates)) {
|
|
4904
5177
|
try {
|
|
4905
|
-
const marker = JSON.parse(await
|
|
5178
|
+
const marker = JSON.parse(await readFile15(file, "utf8"));
|
|
4906
5179
|
const created = Date.parse(marker.created_at);
|
|
4907
5180
|
if (!Number.isFinite(created) || now - created > ttlMs) continue;
|
|
4908
5181
|
if (created > freshestTs) {
|
|
@@ -4952,15 +5225,15 @@ function isRetiredMemory(fm, body = "", now = /* @__PURE__ */ new Date()) {
|
|
|
4952
5225
|
}
|
|
4953
5226
|
|
|
4954
5227
|
// src/sensor-ledger.ts
|
|
4955
|
-
import { createHash as
|
|
4956
|
-
import { existsSync as
|
|
4957
|
-
import { appendFile as appendFile5, mkdir as
|
|
4958
|
-
import
|
|
5228
|
+
import { createHash as createHash4 } from "crypto";
|
|
5229
|
+
import { existsSync as existsSync17, readFileSync as readFileSync2 } from "fs";
|
|
5230
|
+
import { appendFile as appendFile5, mkdir as mkdir13, readFile as readFile16, rename, writeFile as writeFile11 } from "fs/promises";
|
|
5231
|
+
import path19 from "path";
|
|
4959
5232
|
var MAX_LINES = 1e4;
|
|
4960
5233
|
var RETAINED_LINES = 8e3;
|
|
4961
5234
|
var DAY_MS = 864e5;
|
|
4962
5235
|
function sensorLedgerPath(paths) {
|
|
4963
|
-
return
|
|
5236
|
+
return path19.join(paths.runtimeDir, "enforcement", "sensor-ledger.ndjson");
|
|
4964
5237
|
}
|
|
4965
5238
|
function isEvaluation(value) {
|
|
4966
5239
|
if (!value || typeof value !== "object") return false;
|
|
@@ -4971,13 +5244,13 @@ async function appendSensorEvaluations(paths, evaluations) {
|
|
|
4971
5244
|
if (evaluations.length === 0) return;
|
|
4972
5245
|
try {
|
|
4973
5246
|
const file = sensorLedgerPath(paths);
|
|
4974
|
-
await
|
|
5247
|
+
await mkdir13(path19.dirname(file), { recursive: true });
|
|
4975
5248
|
await appendFile5(file, evaluations.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
4976
|
-
const raw = await
|
|
5249
|
+
const raw = await readFile16(file, "utf8");
|
|
4977
5250
|
const lines = raw.split("\n").filter(Boolean);
|
|
4978
5251
|
if (lines.length > MAX_LINES) {
|
|
4979
5252
|
const temp = `${file}.${process.pid}.tmp`;
|
|
4980
|
-
await
|
|
5253
|
+
await writeFile11(temp, lines.slice(-RETAINED_LINES).join("\n") + "\n", "utf8");
|
|
4981
5254
|
await rename(temp, file);
|
|
4982
5255
|
}
|
|
4983
5256
|
} catch {
|
|
@@ -4986,9 +5259,9 @@ async function appendSensorEvaluations(paths, evaluations) {
|
|
|
4986
5259
|
async function loadSensorLedger(paths, opts = {}) {
|
|
4987
5260
|
try {
|
|
4988
5261
|
const file = sensorLedgerPath(paths);
|
|
4989
|
-
if (!
|
|
5262
|
+
if (!existsSync17(file)) return [];
|
|
4990
5263
|
const since = opts.since ? Date.parse(opts.since) : Number.NEGATIVE_INFINITY;
|
|
4991
|
-
const raw = await
|
|
5264
|
+
const raw = await readFile16(file, "utf8");
|
|
4992
5265
|
const out = [];
|
|
4993
5266
|
for (const line of raw.split("\n")) {
|
|
4994
5267
|
if (!line.trim()) continue;
|
|
@@ -5010,11 +5283,11 @@ function computeScopeHash(root, scopedFiles) {
|
|
|
5010
5283
|
try {
|
|
5011
5284
|
const files = [...new Set(scopedFiles.map((f) => f.replace(/\\/g, "/")))].sort();
|
|
5012
5285
|
if (files.length === 0) return "";
|
|
5013
|
-
const hash =
|
|
5286
|
+
const hash = createHash4("sha256");
|
|
5014
5287
|
let included = 0;
|
|
5015
5288
|
for (const rel of files) {
|
|
5016
|
-
const abs =
|
|
5017
|
-
if (!
|
|
5289
|
+
const abs = path19.resolve(root, rel);
|
|
5290
|
+
if (!existsSync17(abs)) continue;
|
|
5018
5291
|
try {
|
|
5019
5292
|
hash.update(rel);
|
|
5020
5293
|
hash.update("\0");
|
|
@@ -5748,8 +6021,8 @@ function normalizeFindingSeverity(raw) {
|
|
|
5748
6021
|
return "info";
|
|
5749
6022
|
}
|
|
5750
6023
|
}
|
|
5751
|
-
function findingKey(tool, ruleId,
|
|
5752
|
-
return `${tool}:${ruleId}:${
|
|
6024
|
+
function findingKey(tool, ruleId, path22) {
|
|
6025
|
+
return `${tool}:${ruleId}:${path22}`;
|
|
5753
6026
|
}
|
|
5754
6027
|
function coerceJson(input) {
|
|
5755
6028
|
if (typeof input === "string") {
|
|
@@ -5783,8 +6056,8 @@ function parseSarif(input) {
|
|
|
5783
6056
|
const physical = asRecord(location.physicalLocation);
|
|
5784
6057
|
const artifact = asRecord(physical.artifactLocation);
|
|
5785
6058
|
const region = asRecord(physical.region);
|
|
5786
|
-
const
|
|
5787
|
-
if (!
|
|
6059
|
+
const path22 = typeof artifact.uri === "string" ? normalizeUri(artifact.uri) : "";
|
|
6060
|
+
if (!path22) continue;
|
|
5788
6061
|
const line = typeof region.startLine === "number" ? region.startLine : void 0;
|
|
5789
6062
|
const snippet = typeof asRecord(region.snippet).text === "string" ? asRecord(region.snippet).text.trim() : void 0;
|
|
5790
6063
|
findings.push({
|
|
@@ -5792,10 +6065,10 @@ function parseSarif(input) {
|
|
|
5792
6065
|
ruleId,
|
|
5793
6066
|
message: message.trim(),
|
|
5794
6067
|
severity,
|
|
5795
|
-
path:
|
|
6068
|
+
path: path22,
|
|
5796
6069
|
...line !== void 0 ? { line } : {},
|
|
5797
6070
|
...snippet ? { snippet } : {},
|
|
5798
|
-
key: findingKey(tool, ruleId,
|
|
6071
|
+
key: findingKey(tool, ruleId, path22)
|
|
5799
6072
|
});
|
|
5800
6073
|
}
|
|
5801
6074
|
}
|
|
@@ -5814,17 +6087,17 @@ function parseSonar(input) {
|
|
|
5814
6087
|
(typeof issue.severity === "string" ? issue.severity : void 0) ?? impactSeverity
|
|
5815
6088
|
);
|
|
5816
6089
|
const component = typeof issue.component === "string" ? issue.component : "";
|
|
5817
|
-
const
|
|
5818
|
-
if (!
|
|
6090
|
+
const path22 = componentToPath(component);
|
|
6091
|
+
if (!path22) continue;
|
|
5819
6092
|
const line = typeof issue.line === "number" ? issue.line : void 0;
|
|
5820
6093
|
findings.push({
|
|
5821
6094
|
tool: "sonar",
|
|
5822
6095
|
ruleId,
|
|
5823
6096
|
message,
|
|
5824
6097
|
severity,
|
|
5825
|
-
path:
|
|
6098
|
+
path: path22,
|
|
5826
6099
|
...line !== void 0 ? { line } : {},
|
|
5827
|
-
key: findingKey("sonar", ruleId,
|
|
6100
|
+
key: findingKey("sonar", ruleId, path22)
|
|
5828
6101
|
});
|
|
5829
6102
|
}
|
|
5830
6103
|
return findings;
|
|
@@ -5837,7 +6110,7 @@ function parseEslintJson(input, opts = {}) {
|
|
|
5837
6110
|
const file = asRecord(fileRaw);
|
|
5838
6111
|
const rawPath = typeof file.filePath === "string" ? file.filePath : "";
|
|
5839
6112
|
if (!rawPath) continue;
|
|
5840
|
-
const
|
|
6113
|
+
const path22 = cwd && rawPath.startsWith(cwd) ? rawPath.slice(cwd.length) : rawPath;
|
|
5841
6114
|
for (const msgRaw of asArray(file.messages)) {
|
|
5842
6115
|
const msg = asRecord(msgRaw);
|
|
5843
6116
|
const ruleId = typeof msg.ruleId === "string" && msg.ruleId ? msg.ruleId : "parse-error";
|
|
@@ -5849,9 +6122,9 @@ function parseEslintJson(input, opts = {}) {
|
|
|
5849
6122
|
ruleId,
|
|
5850
6123
|
message,
|
|
5851
6124
|
severity,
|
|
5852
|
-
path:
|
|
6125
|
+
path: path22,
|
|
5853
6126
|
...line !== void 0 ? { line } : {},
|
|
5854
|
-
key: findingKey("eslint", ruleId,
|
|
6127
|
+
key: findingKey("eslint", ruleId, path22)
|
|
5855
6128
|
});
|
|
5856
6129
|
}
|
|
5857
6130
|
}
|
|
@@ -6301,7 +6574,7 @@ function tallyHotFiles(paths, source = "agent") {
|
|
|
6301
6574
|
if (!norm) continue;
|
|
6302
6575
|
counts.set(norm, (counts.get(norm) ?? 0) + 1);
|
|
6303
6576
|
}
|
|
6304
|
-
return [...counts.entries()].map(([
|
|
6577
|
+
return [...counts.entries()].map(([path22, changes]) => ({ path: path22, changes, source })).sort((a, b) => b.changes - a.changes);
|
|
6305
6578
|
}
|
|
6306
6579
|
function mergeHotFiles(a, b) {
|
|
6307
6580
|
const merged = /* @__PURE__ */ new Map();
|
|
@@ -6321,21 +6594,21 @@ function mergeHotFiles(a, b) {
|
|
|
6321
6594
|
}
|
|
6322
6595
|
|
|
6323
6596
|
// src/eval-history.ts
|
|
6324
|
-
import { appendFile as appendFile6, mkdir as
|
|
6325
|
-
import { existsSync as
|
|
6326
|
-
import
|
|
6597
|
+
import { appendFile as appendFile6, mkdir as mkdir14, readFile as readFile17 } from "fs/promises";
|
|
6598
|
+
import { existsSync as existsSync18 } from "fs";
|
|
6599
|
+
import path20 from "path";
|
|
6327
6600
|
function evalHistoryPath(paths) {
|
|
6328
|
-
return
|
|
6601
|
+
return path20.join(paths.haiveDir, ".cache", "eval-history.jsonl");
|
|
6329
6602
|
}
|
|
6330
6603
|
async function appendEvalHistory(paths, entry) {
|
|
6331
6604
|
const file = evalHistoryPath(paths);
|
|
6332
|
-
await
|
|
6605
|
+
await mkdir14(path20.dirname(file), { recursive: true });
|
|
6333
6606
|
await appendFile6(file, JSON.stringify(entry) + "\n", "utf8");
|
|
6334
6607
|
}
|
|
6335
6608
|
async function loadEvalHistory(paths) {
|
|
6336
6609
|
const file = evalHistoryPath(paths);
|
|
6337
|
-
if (!
|
|
6338
|
-
const raw = await
|
|
6610
|
+
if (!existsSync18(file)) return [];
|
|
6611
|
+
const raw = await readFile17(file, "utf8").catch(() => "");
|
|
6339
6612
|
const out = [];
|
|
6340
6613
|
for (const line of raw.split("\n")) {
|
|
6341
6614
|
const trimmed = line.trim();
|
|
@@ -6678,12 +6951,12 @@ ${trimmed}`;
|
|
|
6678
6951
|
}
|
|
6679
6952
|
|
|
6680
6953
|
// src/handoff.ts
|
|
6681
|
-
import { writeFile as
|
|
6682
|
-
import { existsSync as
|
|
6683
|
-
import
|
|
6954
|
+
import { writeFile as writeFile12, readFile as readFile18, stat as stat4 } from "fs/promises";
|
|
6955
|
+
import { existsSync as existsSync19 } from "fs";
|
|
6956
|
+
import path21 from "path";
|
|
6684
6957
|
var HANDOFF_FILENAME = "NEXT.md";
|
|
6685
6958
|
function handoffFilePath(root) {
|
|
6686
|
-
return
|
|
6959
|
+
return path21.join(root, HANDOFF_FILENAME);
|
|
6687
6960
|
}
|
|
6688
6961
|
function buildHandoffMarkdown(data) {
|
|
6689
6962
|
const at = (data.at ?? /* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6732,20 +7005,20 @@ function buildHandoffMarkdown(data) {
|
|
|
6732
7005
|
}
|
|
6733
7006
|
async function writeSessionHandoff(root, data) {
|
|
6734
7007
|
const file = handoffFilePath(root);
|
|
6735
|
-
await
|
|
7008
|
+
await writeFile12(file, buildHandoffMarkdown(data), "utf8");
|
|
6736
7009
|
return file;
|
|
6737
7010
|
}
|
|
6738
7011
|
async function readSessionHandoff(root) {
|
|
6739
7012
|
const file = handoffFilePath(root);
|
|
6740
|
-
if (!
|
|
6741
|
-
const raw = await
|
|
7013
|
+
if (!existsSync19(file)) return null;
|
|
7014
|
+
const raw = await readFile18(file, "utf8").catch(() => "");
|
|
6742
7015
|
return raw.trim() ? raw : null;
|
|
6743
7016
|
}
|
|
6744
7017
|
async function handoffAgeMs(root, now = /* @__PURE__ */ new Date()) {
|
|
6745
7018
|
const file = handoffFilePath(root);
|
|
6746
|
-
if (!
|
|
7019
|
+
if (!existsSync19(file)) return null;
|
|
6747
7020
|
try {
|
|
6748
|
-
const s = await
|
|
7021
|
+
const s = await stat4(file);
|
|
6749
7022
|
return Math.max(0, now.getTime() - s.mtimeMs);
|
|
6750
7023
|
} catch {
|
|
6751
7024
|
return null;
|
|
@@ -6870,6 +7143,7 @@ export {
|
|
|
6870
7143
|
CODE_MAP_FILE,
|
|
6871
7144
|
CODE_STOPWORDS,
|
|
6872
7145
|
CONFIG_FILE,
|
|
7146
|
+
CONTENT_CATCH_CODES,
|
|
6873
7147
|
CrossRepoProvenanceSchema,
|
|
6874
7148
|
DECAY_DAYS,
|
|
6875
7149
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
@@ -6877,11 +7151,13 @@ export {
|
|
|
6877
7151
|
DEFAULT_CONFIDENCE_THRESHOLDS,
|
|
6878
7152
|
DEFAULT_CONFIG,
|
|
6879
7153
|
DEFAULT_DORMANT_DAYS,
|
|
7154
|
+
DEFAULT_POSTURE,
|
|
6880
7155
|
DEFAULT_PRIORITY_SIGNALS,
|
|
6881
7156
|
ENV_WORKAROUND_TAGS,
|
|
6882
7157
|
FRICTION_FIELD_MAX,
|
|
6883
7158
|
FRICTION_LOG_FILE,
|
|
6884
7159
|
FRICTION_STATE_FILE,
|
|
7160
|
+
GATE_REMINDER_WINDOW_MS,
|
|
6885
7161
|
GUESSABLE_THRESHOLD,
|
|
6886
7162
|
HAIVE_DIR,
|
|
6887
7163
|
HAIVE_OWNED_FILES,
|
|
@@ -6895,6 +7171,8 @@ export {
|
|
|
6895
7171
|
MemoryStatusSchema,
|
|
6896
7172
|
MemoryTypeSchema,
|
|
6897
7173
|
PREVENTION_DEBOUNCE_MS,
|
|
7174
|
+
PREVENTION_RECEIPT_MARKER,
|
|
7175
|
+
PROCESS_GATE_CODES,
|
|
6898
7176
|
PROJECT_CONTEXT_FILE,
|
|
6899
7177
|
PROJECT_CONTEXT_THROTTLE_MS,
|
|
6900
7178
|
REVIEW_LEARNING_MARKER,
|
|
@@ -6904,6 +7182,7 @@ export {
|
|
|
6904
7182
|
SENSOR_ABSENT_LOOKBACK,
|
|
6905
7183
|
SENSOR_ABSENT_WINDOW,
|
|
6906
7184
|
SESSION_RECAP_TTL_MS,
|
|
7185
|
+
SETUP_GATE_CODES,
|
|
6907
7186
|
STACK_PACK_TAG,
|
|
6908
7187
|
SensorSchema,
|
|
6909
7188
|
TEST_FRAMEWORKS,
|
|
@@ -6936,6 +7215,7 @@ export {
|
|
|
6936
7215
|
briefingMarkerPath,
|
|
6937
7216
|
briefingMarkersDir,
|
|
6938
7217
|
briefingProofLine,
|
|
7218
|
+
buildBaselineHealthFinding,
|
|
6939
7219
|
buildCodeMap,
|
|
6940
7220
|
buildCoverageIndex,
|
|
6941
7221
|
buildDashboard,
|
|
@@ -6947,6 +7227,7 @@ export {
|
|
|
6947
7227
|
buildReport,
|
|
6948
7228
|
bumpRead,
|
|
6949
7229
|
classifyMemoryPriority,
|
|
7230
|
+
codeMapContentHash,
|
|
6950
7231
|
codeMapPath,
|
|
6951
7232
|
collectTimelineEntries,
|
|
6952
7233
|
compactAutoRecapBody,
|
|
@@ -6955,6 +7236,7 @@ export {
|
|
|
6955
7236
|
compareImpact,
|
|
6956
7237
|
compileRegexSensor,
|
|
6957
7238
|
componentOf,
|
|
7239
|
+
computeBaselineHealth,
|
|
6958
7240
|
computeEvalTrend,
|
|
6959
7241
|
computeGatePrecision,
|
|
6960
7242
|
computeImpact,
|
|
@@ -6964,8 +7246,11 @@ export {
|
|
|
6964
7246
|
configPath,
|
|
6965
7247
|
contractLockPath,
|
|
6966
7248
|
countSourceFilesOnDisk,
|
|
7249
|
+
decideVerdict,
|
|
7250
|
+
dedupeRefusals,
|
|
6967
7251
|
deriveConfidence,
|
|
6968
7252
|
deriveMainAreas,
|
|
7253
|
+
describePosture,
|
|
6969
7254
|
detectAgentContext,
|
|
6970
7255
|
detectSensorWeakening,
|
|
6971
7256
|
detectStacksFromManifests,
|
|
@@ -6981,6 +7266,7 @@ export {
|
|
|
6981
7266
|
evalHistoryPath,
|
|
6982
7267
|
evaluateSkillActivation,
|
|
6983
7268
|
existingGateMissShas,
|
|
7269
|
+
explainSensorRejection,
|
|
6984
7270
|
extractActionsBriefBody,
|
|
6985
7271
|
extractCorrectApproachExamples,
|
|
6986
7272
|
extractReferencedPaths,
|
|
@@ -7098,6 +7384,7 @@ export {
|
|
|
7098
7384
|
readUsageEvents,
|
|
7099
7385
|
recommendFeedbackAdjustment,
|
|
7100
7386
|
recordApplied,
|
|
7387
|
+
recordGateReminder,
|
|
7101
7388
|
recordPrevention,
|
|
7102
7389
|
recordPreventionHits,
|
|
7103
7390
|
recordProjectContextEmission,
|
|
@@ -7106,10 +7393,12 @@ export {
|
|
|
7106
7393
|
renderBehaviourCoverageLine,
|
|
7107
7394
|
renderBootstrapChecklist,
|
|
7108
7395
|
renderCaughtForYou,
|
|
7396
|
+
renderPreventionComment,
|
|
7109
7397
|
renderPreventionReceipt,
|
|
7110
7398
|
renderPreventionReceiptShare,
|
|
7111
7399
|
resolveBriefingBudget,
|
|
7112
7400
|
resolveConfigPath,
|
|
7401
|
+
resolveGatePolicy,
|
|
7113
7402
|
resolveHaivePaths,
|
|
7114
7403
|
resolveManifestFiles,
|
|
7115
7404
|
resolveProjectInfo,
|
|
@@ -7137,8 +7426,10 @@ export {
|
|
|
7137
7426
|
sensorPromotedAtMap,
|
|
7138
7427
|
sensorSelfCheck,
|
|
7139
7428
|
sensorTargetsFromDiff,
|
|
7429
|
+
serializeCodeMap,
|
|
7140
7430
|
serializeMemory,
|
|
7141
7431
|
setFrictionStatus,
|
|
7432
|
+
shouldExpandGateReminder,
|
|
7142
7433
|
snapshotContract,
|
|
7143
7434
|
specificityScore,
|
|
7144
7435
|
stripPrivate,
|