@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.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, path21) {
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(path21);
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 mkdir4, readFile as readFile6, readdir as readdir3, writeFile as writeFile3 } from "fs/promises";
2653
- import { existsSync as existsSync6 } from "fs";
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 path8 from "path";
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 path8.join(paths.haiveDir, CODE_MAP_FILE);
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 (!existsSync6(file)) return null;
3072
- return JSON.parse(await readFile6(file, "utf8"));
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
- await mkdir4(path8.dirname(file), { recursive: true });
3077
- await writeFile3(file, JSON.stringify(map, null, 2), "utf8");
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 = path8.relative(root, abs).replace(/\\/g, "/");
3357
+ const rel = path9.relative(root, abs).replace(/\\/g, "/");
3085
3358
  if (rel.startsWith(".ai/")) continue;
3086
- const content = await readFile6(abs, "utf8");
3087
- const ext = path8.extname(abs).toLowerCase();
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 path8.join(root, rel);
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 path8.join(nested, rel);
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 = path8.join(root, entry.name);
3132
- if (existsSync6(path8.join(full, ".git"))) {
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 = path8.join(dir, entry.name);
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 = path8.extname(entry.name).toLowerCase();
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 = path8.extname(base).toLowerCase();
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 existsSync7 } from "fs";
3711
+ import { existsSync as existsSync8 } from "fs";
3439
3712
  import { readFileSync } from "fs";
3440
- import { readFile as readFile7, rm, writeFile as writeFile4 } from "fs/promises";
3441
- import path9 from "path";
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 path9.join(paths.haiveDir, CONFIG_FILE);
3810
+ return path10.join(paths.haiveDir, CONFIG_FILE);
3538
3811
  }
3539
3812
  function resolveConfigPath(paths) {
3540
3813
  const current = configPath(paths);
3541
- if (existsSync7(current)) return current;
3542
- const legacy = path9.join(paths.haiveDir, LEGACY_CONFIG_FILE);
3543
- return existsSync7(legacy) ? legacy : current;
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 (!existsSync7(file)) return { ...DEFAULT_CONFIG };
3820
+ if (!existsSync8(file)) return { ...DEFAULT_CONFIG };
3548
3821
  try {
3549
- const raw = await readFile7(file, "utf8");
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 (!existsSync7(file)) return { ...DEFAULT_CONFIG };
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 writeFile4(configPath(paths), JSON.stringify(config, null, 2) + "\n", "utf8");
3573
- const legacy = path9.join(paths.haiveDir, LEGACY_CONFIG_FILE);
3574
- if (existsSync7(legacy)) {
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 existsSync8 } from "fs";
3598
- import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
3599
- import path10 from "path";
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 = path10.join(cacheDir, "import-map.json");
3603
- if (!existsSync8(mapPath)) return {};
3875
+ const mapPath = path11.join(cacheDir, "import-map.json");
3876
+ if (!existsSync9(mapPath)) return {};
3604
3877
  try {
3605
- return JSON.parse(await readFile8(mapPath, "utf8"));
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 writeFile5(path10.join(cacheDir, "import-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
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 = path10.resolve(projectRoot, source.path);
3633
- if (!existsSync8(resolved)) {
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 (!existsSync8(sourcePaths.memoriesDir)) {
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 = path10.join(paths.memoriesDir, "shared", source.name);
3670
- await mkdir5(destDir, { recursive: true });
3671
- const cacheDir = path10.join(paths.haiveDir, ".cache", "cross-repo", source.name);
3672
- await mkdir5(cacheDir, { recursive: true });
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 && existsSync8(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 writeFile5(
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 = path10.join(destDir, `${newFm.id}.md`);
3722
- await writeFile5(destPath, serializeMemory({ frontmatter: newFm, body }), "utf8");
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 = path10.join(paths.haiveDir, ".cache", "cross-repo", source.name);
3733
- await mkdir5(cacheDir, { recursive: true });
3734
- if (existsSync8(path10.join(cacheDir, ".git"))) {
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 existsSync9 } from "fs";
3760
- import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
3761
- import path11 from "path";
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 = path11.basename(file);
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) => path11.resolve(projectRoot, f)).filter(existsSync9);
4124
+ return configuredFiles.map((f) => path12.resolve(projectRoot, f)).filter(existsSync10);
3852
4125
  }
3853
- return KNOWN_MANIFESTS.map(({ name }) => path11.join(projectRoot, name)).filter(existsSync9);
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 = path11.join(haiveDir, "contracts");
3857
- await mkdir6(contractsDir, { recursive: true });
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 readFile9(manifestPath, "utf8");
4135
+ const content = await readFile10(manifestPath, "utf8");
3863
4136
  const currentDeps = parser2(content);
3864
- const lockName = `deps-${path11.basename(manifestPath)}.lock`;
3865
- const lockPath = path11.join(contractsDir, lockName);
3866
- if (!existsSync9(lockPath)) {
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: path11.relative(projectRoot, manifestPath),
3869
- format: path11.basename(manifestPath),
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 writeFile6(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
4146
+ await writeFile7(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
3874
4147
  continue;
3875
4148
  }
3876
- const snapshot = JSON.parse(await readFile9(lockPath, "utf8"));
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: path11.relative(projectRoot, manifestPath), changes });
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 writeFile6(lockPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
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 existsSync10 } from "fs";
3904
- import { readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
3905
- import path12 from "path";
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 path12.join(haiveDir, "contracts", `${name}.lock`);
4381
+ return path13.join(haiveDir, "contracts", `${name}.lock`);
4109
4382
  }
4110
4383
  async function snapshotContract(projectRoot, haiveDir, contract) {
4111
- const filePath = path12.resolve(projectRoot, contract.path);
4112
- if (!existsSync10(filePath)) {
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 readFile10(filePath, "utf8");
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 = path12.join(haiveDir, "contracts");
4126
- await mkdir7(contractsDir, { recursive: true });
4127
- await writeFile7(contractLockPath(haiveDir, contract.name), JSON.stringify(snapshot, null, 2) + "\n", "utf8");
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 = path12.resolve(projectRoot, contract.path);
4132
- if (!existsSync10(filePath)) {
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 (!existsSync10(lockPath)) {
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 readFile10(filePath, "utf8");
4141
- const beforeSnapshot = JSON.parse(await readFile10(lockPath, "utf8"));
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 writeFile7(lockPath, JSON.stringify(afterSnapshot, null, 2) + "\n", "utf8");
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 mkdir8, readFile as readFile11, stat as stat2 } from "fs/promises";
4170
- import { existsSync as existsSync11 } from "fs";
4171
- import path13 from "path";
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 path13.join(paths.haiveDir, USAGE_LOG_DIR, USAGE_LOG_FILE);
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 = path13.dirname(file);
4181
- if (!existsSync11(dir)) await mkdir8(dir, { recursive: true });
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 (!existsSync11(file)) return [];
4189
- const raw = await readFile11(file, "utf8");
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 (!existsSync11(file)) return { exists: false, size_bytes: 0, lines: 0 };
4239
- const st = await stat2(file);
4240
- const raw = await readFile11(file, "utf8");
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 mkdir9, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
4246
- import { existsSync as existsSync12 } from "fs";
4247
- import { createHash as createHash2 } from "crypto";
4248
- import path14 from "path";
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 path14.join(paths.runtimeDir, FRICTION_LOG_FILE);
4526
+ return path15.join(paths.runtimeDir, FRICTION_LOG_FILE);
4254
4527
  }
4255
4528
  function frictionStatePath(paths) {
4256
- return path14.join(paths.runtimeDir, FRICTION_STATE_FILE);
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 createHash2("sha256").update(basis).digest("hex").slice(0, 16);
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 (!existsSync12(paths.runtimeDir)) await mkdir9(paths.runtimeDir, { recursive: true });
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 (!existsSync12(file)) return [];
4588
+ if (!existsSync13(file)) return [];
4316
4589
  let raw;
4317
4590
  try {
4318
- raw = await readFile12(file, "utf8");
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 (!existsSync12(file)) return {};
4608
+ if (!existsSync13(file)) return {};
4336
4609
  try {
4337
- const parsed = JSON.parse(await readFile12(file, "utf8"));
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 (!existsSync12(paths.runtimeDir)) await mkdir9(paths.runtimeDir, { recursive: true });
4345
- await writeFile8(frictionStatePath(paths), JSON.stringify(state, null, 2) + "\n", "utf8");
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 existsSync13 } from "fs";
4483
- import path15 from "path";
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 (existsSync13(path15.join(root, m))) found.push(m);
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 = path15.resolve(opts.cwd ?? process.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 !== "" ? path15.resolve(raw) : null;
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: existsSync13(paths.haiveDir),
4505
- memories_dir_exists: existsSync13(paths.memoriesDir),
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 mkdir10, readFile as readFile13, appendFile as appendFile4 } from "fs/promises";
4761
- import { existsSync as existsSync14 } from "fs";
4762
- import path16 from "path";
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 path16.join(paths.runtimeDir, RUNTIME_JOURNAL_FILENAME);
5038
+ return path17.join(paths.runtimeDir, RUNTIME_JOURNAL_FILENAME);
4766
5039
  }
4767
5040
  async function appendRuntimeJournalEntry(paths, entry) {
4768
5041
  try {
4769
- await mkdir10(paths.runtimeDir, { recursive: true });
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 (!existsSync14(file) || limit <= 0) return [];
5060
+ if (!existsSync15(file) || limit <= 0) return [];
4788
5061
  try {
4789
- const raw = await readFile13(file, "utf8");
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 mkdir11, readdir as readdir4, readFile as readFile14, writeFile as writeFile9 } from "fs/promises";
4806
- import { existsSync as existsSync15 } from "fs";
4807
- import path17 from "path";
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 path17.join(paths.runtimeDir, "enforcement");
5084
+ return path18.join(paths.runtimeDir, "enforcement");
4812
5085
  }
4813
5086
  function briefingMarkersDir(paths) {
4814
- return path17.join(enforcementDir(paths), "briefings");
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 path17.join(briefingMarkersDir(paths), `${normalizeSessionId(sessionId)}.json`);
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 mkdir11(briefingMarkersDir(paths), { recursive: true });
4846
- await writeFile9(
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 (!existsSync15(file)) return null;
5128
+ if (!existsSync16(file)) return null;
4856
5129
  try {
4857
- const marker = JSON.parse(await readFile14(file, "utf8"));
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 (existsSync15(exact)) candidates.push(exact);
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(path17.join(dir, file));
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 readFile14(file, "utf8"));
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 (existsSync15(exact)) candidates.push(exact);
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(path17.join(dir, file));
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 readFile14(file, "utf8"));
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 createHash3 } from "crypto";
4956
- import { existsSync as existsSync16, readFileSync as readFileSync2 } from "fs";
4957
- import { appendFile as appendFile5, mkdir as mkdir12, readFile as readFile15, rename, writeFile as writeFile10 } from "fs/promises";
4958
- import path18 from "path";
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 path18.join(paths.runtimeDir, "enforcement", "sensor-ledger.ndjson");
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 mkdir12(path18.dirname(file), { recursive: true });
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 readFile15(file, "utf8");
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 writeFile10(temp, lines.slice(-RETAINED_LINES).join("\n") + "\n", "utf8");
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 (!existsSync16(file)) return [];
5262
+ if (!existsSync17(file)) return [];
4990
5263
  const since = opts.since ? Date.parse(opts.since) : Number.NEGATIVE_INFINITY;
4991
- const raw = await readFile15(file, "utf8");
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 = createHash3("sha256");
5286
+ const hash = createHash4("sha256");
5014
5287
  let included = 0;
5015
5288
  for (const rel of files) {
5016
- const abs = path18.resolve(root, rel);
5017
- if (!existsSync16(abs)) continue;
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, path21) {
5752
- return `${tool}:${ruleId}:${path21}`;
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 path21 = typeof artifact.uri === "string" ? normalizeUri(artifact.uri) : "";
5787
- if (!path21) continue;
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: path21,
6068
+ path: path22,
5796
6069
  ...line !== void 0 ? { line } : {},
5797
6070
  ...snippet ? { snippet } : {},
5798
- key: findingKey(tool, ruleId, path21)
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 path21 = componentToPath(component);
5818
- if (!path21) continue;
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: path21,
6098
+ path: path22,
5826
6099
  ...line !== void 0 ? { line } : {},
5827
- key: findingKey("sonar", ruleId, path21)
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 path21 = cwd && rawPath.startsWith(cwd) ? rawPath.slice(cwd.length) : rawPath;
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: path21,
6125
+ path: path22,
5853
6126
  ...line !== void 0 ? { line } : {},
5854
- key: findingKey("eslint", ruleId, path21)
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(([path21, changes]) => ({ path: path21, changes, source })).sort((a, b) => b.changes - a.changes);
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 mkdir13, readFile as readFile16 } from "fs/promises";
6325
- import { existsSync as existsSync17 } from "fs";
6326
- import path19 from "path";
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 path19.join(paths.haiveDir, ".cache", "eval-history.jsonl");
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 mkdir13(path19.dirname(file), { recursive: true });
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 (!existsSync17(file)) return [];
6338
- const raw = await readFile16(file, "utf8").catch(() => "");
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 writeFile11, readFile as readFile17, stat as stat3 } from "fs/promises";
6682
- import { existsSync as existsSync18 } from "fs";
6683
- import path20 from "path";
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 path20.join(root, HANDOFF_FILENAME);
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 writeFile11(file, buildHandoffMarkdown(data), "utf8");
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 (!existsSync18(file)) return null;
6741
- const raw = await readFile17(file, "utf8").catch(() => "");
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 (!existsSync18(file)) return null;
7019
+ if (!existsSync19(file)) return null;
6747
7020
  try {
6748
- const s = await stat3(file);
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,