@hiveai/core 0.17.0 → 0.18.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 +230 -16
- package/dist/index.js +449 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -800,6 +800,45 @@ function summarizeImpact(scores) {
|
|
|
800
800
|
}
|
|
801
801
|
return summary;
|
|
802
802
|
}
|
|
803
|
+
function recommendFeedbackAdjustment(fm, usage, options = {}) {
|
|
804
|
+
const rejectionThreshold = options.rejectionThreshold ?? 2;
|
|
805
|
+
const hasPositiveOutcome = usage.applied_count > 0 || usage.prevented_count > 0;
|
|
806
|
+
if (fm.sensor?.severity === "block" && usage.rejected_count >= 1) {
|
|
807
|
+
return {
|
|
808
|
+
action: "downgrade-block-sensor",
|
|
809
|
+
reason: "A human contested a blocking guardrail; downgrade it to warn so the gate stays helpful while the lesson is reviewed."
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (!hasPositiveOutcome && usage.rejected_count >= rejectionThreshold) {
|
|
813
|
+
return {
|
|
814
|
+
action: "deprecate-memory",
|
|
815
|
+
reason: `${usage.rejected_count} rejection(s) and no applied/prevented outcomes; deprecate until the lesson is refined.`
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
return { action: "none", reason: "No automatic adjustment needed." };
|
|
819
|
+
}
|
|
820
|
+
function applyFeedbackAdjustment(fm, adjustment, now = /* @__PURE__ */ new Date()) {
|
|
821
|
+
if (adjustment.action === "none") return fm;
|
|
822
|
+
const tags = [.../* @__PURE__ */ new Set([...fm.tags, "feedback-contested"])];
|
|
823
|
+
if (adjustment.action === "downgrade-block-sensor" && fm.sensor) {
|
|
824
|
+
return {
|
|
825
|
+
...fm,
|
|
826
|
+
tags,
|
|
827
|
+
verified_at: now.toISOString(),
|
|
828
|
+
sensor: { ...fm.sensor, severity: "warn" }
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
if (adjustment.action === "deprecate-memory") {
|
|
832
|
+
return {
|
|
833
|
+
...fm,
|
|
834
|
+
tags,
|
|
835
|
+
status: "deprecated",
|
|
836
|
+
stale_reason: adjustment.reason,
|
|
837
|
+
verified_at: now.toISOString()
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
return fm;
|
|
841
|
+
}
|
|
803
842
|
|
|
804
843
|
// src/prevention.ts
|
|
805
844
|
import { appendFile, mkdir as mkdir2, readFile as readFile4 } from "fs/promises";
|
|
@@ -865,6 +904,84 @@ function computeRecurrence(events) {
|
|
|
865
904
|
rows.sort((a, b) => b.distinct_days - a.distinct_days || b.catches - a.catches);
|
|
866
905
|
return { recurring_count: rows.length, top: rows };
|
|
867
906
|
}
|
|
907
|
+
function briefingProofLine(events, options = {}) {
|
|
908
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
909
|
+
const days = options.days ?? 30;
|
|
910
|
+
const since = now.getTime() - days * MS_PER_DAY2;
|
|
911
|
+
let count = 0;
|
|
912
|
+
for (const e of events) {
|
|
913
|
+
const t = Date.parse(e.at);
|
|
914
|
+
if (!Number.isFinite(t)) continue;
|
|
915
|
+
if (t >= since && t <= now.getTime()) count += 1;
|
|
916
|
+
}
|
|
917
|
+
if (count === 0) return null;
|
|
918
|
+
return `This harness prevented ${count} repeated mistake${count === 1 ? "" : "s"} in the last ${days} days.`;
|
|
919
|
+
}
|
|
920
|
+
function titleFromMemory(loaded) {
|
|
921
|
+
if (!loaded) return "";
|
|
922
|
+
for (const line of loaded.memory.body.split("\n")) {
|
|
923
|
+
const heading = /^#+\s*(.+)$/.exec(line.trim());
|
|
924
|
+
if (heading) return heading[1].trim().slice(0, 96);
|
|
925
|
+
}
|
|
926
|
+
for (const line of loaded.memory.body.split("\n")) {
|
|
927
|
+
const t = line.trim();
|
|
928
|
+
if (t) return t.replace(/^[-*]\s*/, "").slice(0, 96);
|
|
929
|
+
}
|
|
930
|
+
return "";
|
|
931
|
+
}
|
|
932
|
+
function sourceRank(source) {
|
|
933
|
+
return source === "anti-pattern" ? 0 : 1;
|
|
934
|
+
}
|
|
935
|
+
function summarizeCaughtForYou(events, memories, usage, options = {}) {
|
|
936
|
+
const until = options.now ?? /* @__PURE__ */ new Date();
|
|
937
|
+
const sinceMs = options.since === void 0 ? null : options.since instanceof Date ? options.since.getTime() : Date.parse(options.since);
|
|
938
|
+
const untilMs = until.getTime();
|
|
939
|
+
const byIdSource = /* @__PURE__ */ new Map();
|
|
940
|
+
for (const e of events) {
|
|
941
|
+
const t = Date.parse(e.at);
|
|
942
|
+
if (!Number.isFinite(t)) continue;
|
|
943
|
+
if (sinceMs !== null && Number.isFinite(sinceMs) && t < sinceMs) continue;
|
|
944
|
+
if (t > untilMs) continue;
|
|
945
|
+
const key = `${e.id}\0${e.source}`;
|
|
946
|
+
const current = byIdSource.get(key) ?? { id: e.id, source: e.source, catches: 0, last_at: e.at };
|
|
947
|
+
current.catches += 1;
|
|
948
|
+
if (e.at > current.last_at) current.last_at = e.at;
|
|
949
|
+
byIdSource.set(key, current);
|
|
950
|
+
}
|
|
951
|
+
const memoryById = new Map(memories.map((m) => [m.memory.frontmatter.id, m]));
|
|
952
|
+
const rows = [...byIdSource.values()].map((row) => {
|
|
953
|
+
const current = getUsage(usage, row.id).prevented_count;
|
|
954
|
+
const previous = Math.max(0, current - row.catches);
|
|
955
|
+
return {
|
|
956
|
+
id: row.id,
|
|
957
|
+
title: titleFromMemory(memoryById.get(row.id)) || row.id,
|
|
958
|
+
source: row.source,
|
|
959
|
+
catches: row.catches,
|
|
960
|
+
previous_count: previous,
|
|
961
|
+
current_count: current,
|
|
962
|
+
last_at: row.last_at
|
|
963
|
+
};
|
|
964
|
+
}).sort((a, b) => b.last_at.localeCompare(a.last_at) || sourceRank(a.source) - sourceRank(b.source)).slice(0, options.limit ?? 5);
|
|
965
|
+
return {
|
|
966
|
+
total_catches: [...byIdSource.values()].reduce((sum, row) => sum + row.catches, 0),
|
|
967
|
+
since: sinceMs !== null && Number.isFinite(sinceMs) ? new Date(sinceMs).toISOString() : null,
|
|
968
|
+
until: until.toISOString(),
|
|
969
|
+
rows
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function renderCaughtForYou(summary) {
|
|
973
|
+
if (summary.total_catches === 0 || summary.rows.length === 0) return null;
|
|
974
|
+
const lines = [
|
|
975
|
+
`Caught for you: ${summary.total_catches} prevented repeat${summary.total_catches === 1 ? "" : "s"} this session.`
|
|
976
|
+
];
|
|
977
|
+
for (const row of summary.rows) {
|
|
978
|
+
const gate = row.source === "anti-pattern" ? "Blocked" : "Caught";
|
|
979
|
+
lines.push(
|
|
980
|
+
`- ${gate}: ${row.title} (${row.id}). Prevention ${row.previous_count}->${row.current_count}.`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
return lines.join("\n");
|
|
984
|
+
}
|
|
868
985
|
|
|
869
986
|
// src/context-throttle.ts
|
|
870
987
|
import { createHash } from "crypto";
|
|
@@ -1697,7 +1814,8 @@ var DEFAULT_CONFIG = {
|
|
|
1697
1814
|
scoreThreshold: 80,
|
|
1698
1815
|
cleanupGeneratedArtifacts: true,
|
|
1699
1816
|
toolProfile: "enforcement",
|
|
1700
|
-
policyPacks: ["architecture", "gotchas", "security", "domain", "release"]
|
|
1817
|
+
policyPacks: ["architecture", "gotchas", "security", "domain", "release"],
|
|
1818
|
+
releaseBranch: "main"
|
|
1701
1819
|
}
|
|
1702
1820
|
};
|
|
1703
1821
|
var AUTOPILOT_DEFAULTS = {
|
|
@@ -1725,7 +1843,8 @@ var AUTOPILOT_DEFAULTS = {
|
|
|
1725
1843
|
scoreThreshold: 85,
|
|
1726
1844
|
cleanupGeneratedArtifacts: true,
|
|
1727
1845
|
toolProfile: "enforcement",
|
|
1728
|
-
policyPacks: ["architecture", "gotchas", "security", "domain", "release"]
|
|
1846
|
+
policyPacks: ["architecture", "gotchas", "security", "domain", "release"],
|
|
1847
|
+
releaseBranch: "main"
|
|
1729
1848
|
}
|
|
1730
1849
|
};
|
|
1731
1850
|
function antiPatternGateParams(gate) {
|
|
@@ -3358,8 +3477,75 @@ function parseSonar(input) {
|
|
|
3358
3477
|
}
|
|
3359
3478
|
return findings;
|
|
3360
3479
|
}
|
|
3361
|
-
function
|
|
3362
|
-
|
|
3480
|
+
function parseEslintJson(input, opts = {}) {
|
|
3481
|
+
const docs = asArray(coerceJson(input));
|
|
3482
|
+
const findings = [];
|
|
3483
|
+
const cwd = opts.cwd ? opts.cwd.replace(/\/+$/, "") + "/" : "";
|
|
3484
|
+
for (const fileRaw of docs) {
|
|
3485
|
+
const file = asRecord(fileRaw);
|
|
3486
|
+
const rawPath = typeof file.filePath === "string" ? file.filePath : "";
|
|
3487
|
+
if (!rawPath) continue;
|
|
3488
|
+
const path18 = cwd && rawPath.startsWith(cwd) ? rawPath.slice(cwd.length) : rawPath;
|
|
3489
|
+
for (const msgRaw of asArray(file.messages)) {
|
|
3490
|
+
const msg = asRecord(msgRaw);
|
|
3491
|
+
const ruleId = typeof msg.ruleId === "string" && msg.ruleId ? msg.ruleId : "parse-error";
|
|
3492
|
+
const message = typeof msg.message === "string" ? msg.message.trim() : ruleId;
|
|
3493
|
+
const severity = normalizeFindingSeverity(msg.severity === 2 ? "error" : "warning");
|
|
3494
|
+
const line = typeof msg.line === "number" ? msg.line : void 0;
|
|
3495
|
+
findings.push({
|
|
3496
|
+
tool: "eslint",
|
|
3497
|
+
ruleId,
|
|
3498
|
+
message,
|
|
3499
|
+
severity,
|
|
3500
|
+
path: path18,
|
|
3501
|
+
...line !== void 0 ? { line } : {},
|
|
3502
|
+
key: findingKey("eslint", ruleId, path18)
|
|
3503
|
+
});
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
return findings;
|
|
3507
|
+
}
|
|
3508
|
+
var NPM_AUDIT_SEVERITY = {
|
|
3509
|
+
critical: "blocker",
|
|
3510
|
+
high: "critical",
|
|
3511
|
+
moderate: "major",
|
|
3512
|
+
low: "minor",
|
|
3513
|
+
info: "info"
|
|
3514
|
+
};
|
|
3515
|
+
function parseNpmAudit(input) {
|
|
3516
|
+
const doc = asRecord(coerceJson(input));
|
|
3517
|
+
const vulns = asRecord(doc.vulnerabilities);
|
|
3518
|
+
const findings = [];
|
|
3519
|
+
for (const [name, vulnRaw] of Object.entries(vulns)) {
|
|
3520
|
+
const vuln = asRecord(vulnRaw);
|
|
3521
|
+
const sev = typeof vuln.severity === "string" ? vuln.severity.toLowerCase() : "info";
|
|
3522
|
+
const severity = NPM_AUDIT_SEVERITY[sev] ?? "info";
|
|
3523
|
+
const via = asArray(vuln.via);
|
|
3524
|
+
const firstAdvisory = via.map(asRecord).find((v) => typeof v.title === "string");
|
|
3525
|
+
const title = firstAdvisory && typeof firstAdvisory.title === "string" ? firstAdvisory.title : `Vulnerable dependency: ${name}`;
|
|
3526
|
+
const range = typeof vuln.range === "string" ? ` (affected range: ${vuln.range})` : "";
|
|
3527
|
+
findings.push({
|
|
3528
|
+
tool: "npm-audit",
|
|
3529
|
+
ruleId: name,
|
|
3530
|
+
message: `${title}${range}`,
|
|
3531
|
+
severity,
|
|
3532
|
+
path: "package.json",
|
|
3533
|
+
key: findingKey("npm-audit", name, "package.json")
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
return findings;
|
|
3537
|
+
}
|
|
3538
|
+
function parseFindings(format, input, opts = {}) {
|
|
3539
|
+
switch (format) {
|
|
3540
|
+
case "sonar":
|
|
3541
|
+
return parseSonar(input);
|
|
3542
|
+
case "eslint":
|
|
3543
|
+
return parseEslintJson(input, opts);
|
|
3544
|
+
case "npm-audit":
|
|
3545
|
+
return parseNpmAudit(input);
|
|
3546
|
+
default:
|
|
3547
|
+
return parseSarif(input);
|
|
3548
|
+
}
|
|
3363
3549
|
}
|
|
3364
3550
|
function normalizeUri(uri) {
|
|
3365
3551
|
return uri.replace(/^file:\/\//, "").replace(/^\.\//, "");
|
|
@@ -3475,6 +3661,23 @@ function suggestGate(precision, rejections, currentGate) {
|
|
|
3475
3661
|
}
|
|
3476
3662
|
return null;
|
|
3477
3663
|
}
|
|
3664
|
+
function nullableMetricDelta(baseline, current) {
|
|
3665
|
+
if (baseline === null || current === null) return { baseline, current, delta: null };
|
|
3666
|
+
return { baseline: round33(baseline), current: round33(current), delta: round33(current - baseline) };
|
|
3667
|
+
}
|
|
3668
|
+
function compareGatePrecision(baseline, current) {
|
|
3669
|
+
const precision = nullableMetricDelta(baseline.precision, current.precision);
|
|
3670
|
+
const rejections = nullableMetricDelta(baseline.rejections, current.rejections);
|
|
3671
|
+
const falsePositivesIncreased = current.rejections > baseline.rejections;
|
|
3672
|
+
const precisionRegressed = precision.delta !== null && precision.delta < 0;
|
|
3673
|
+
return {
|
|
3674
|
+
precision,
|
|
3675
|
+
rejections,
|
|
3676
|
+
false_positives_increased: falsePositivesIncreased,
|
|
3677
|
+
precision_regressed: precisionRegressed,
|
|
3678
|
+
regressed: falsePositivesIncreased || precisionRegressed
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3478
3681
|
|
|
3479
3682
|
// src/dashboard.ts
|
|
3480
3683
|
var MS_PER_DAY4 = 24 * 60 * 60 * 1e3;
|
|
@@ -3583,9 +3786,26 @@ function buildDashboard(memories, usage, options = {}) {
|
|
|
3583
3786
|
);
|
|
3584
3787
|
sensorRows.sort((a, b) => b.last_fired.localeCompare(a.last_fired));
|
|
3585
3788
|
dormantRows.sort((a, b) => b.age_days - a.age_days);
|
|
3789
|
+
const eventLog = options.preventionEvents ?? [];
|
|
3790
|
+
const recurrence = computeRecurrence(eventLog);
|
|
3586
3791
|
return {
|
|
3587
3792
|
generated_at: now.toISOString(),
|
|
3588
3793
|
inventory,
|
|
3794
|
+
prevention: {
|
|
3795
|
+
total_events: preventionEvents,
|
|
3796
|
+
memories_with_catches: preventionRows.length,
|
|
3797
|
+
top: preventionRows.sort((a, b) => b.prevented_count - a.prevented_count).slice(0, top),
|
|
3798
|
+
trend: computePreventionTrend(eventLog, now),
|
|
3799
|
+
recurrence: {
|
|
3800
|
+
...recurrence,
|
|
3801
|
+
top: recurrence.top.slice(0, top)
|
|
3802
|
+
}
|
|
3803
|
+
},
|
|
3804
|
+
gate_precision: computeGatePrecision(
|
|
3805
|
+
eventLog,
|
|
3806
|
+
usage,
|
|
3807
|
+
options.antiPatternGate ?? "anchored"
|
|
3808
|
+
),
|
|
3589
3809
|
impact: { ...summarizeImpact(impactScores), top: impactRows.slice(0, top) },
|
|
3590
3810
|
sensors: {
|
|
3591
3811
|
total: sensorTotal,
|
|
@@ -3607,21 +3827,6 @@ function buildDashboard(memories, usage, options = {}) {
|
|
|
3607
3827
|
decaying,
|
|
3608
3828
|
top_dormant: dormantRows.slice(0, top)
|
|
3609
3829
|
},
|
|
3610
|
-
prevention: {
|
|
3611
|
-
total_events: preventionEvents,
|
|
3612
|
-
memories_with_catches: preventionRows.length,
|
|
3613
|
-
top: preventionRows.sort((a, b) => b.prevented_count - a.prevented_count).slice(0, top),
|
|
3614
|
-
trend: computePreventionTrend(options.preventionEvents ?? [], now),
|
|
3615
|
-
recurrence: {
|
|
3616
|
-
...computeRecurrence(options.preventionEvents ?? []),
|
|
3617
|
-
top: computeRecurrence(options.preventionEvents ?? []).top.slice(0, top)
|
|
3618
|
-
}
|
|
3619
|
-
},
|
|
3620
|
-
gate_precision: computeGatePrecision(
|
|
3621
|
-
options.preventionEvents ?? [],
|
|
3622
|
-
usage,
|
|
3623
|
-
options.antiPatternGate ?? "anchored"
|
|
3624
|
-
),
|
|
3625
3830
|
corpus: {
|
|
3626
3831
|
memory_files: inventory.total,
|
|
3627
3832
|
body_chars: bodyChars,
|
|
@@ -3794,6 +3999,7 @@ function planConflictResolution(a, b) {
|
|
|
3794
3999
|
var REVERT_RE = /^Revert\s+"(.+)"\s*$/i;
|
|
3795
4000
|
var FIXUP_RE = /^(?:fixup!|hotfix[:!]|fix[:!]\s*revert|revert\s+revert)/i;
|
|
3796
4001
|
var URGENT_FIX_RE = /\b(hotfix|urgent fix|emergency fix|critical fix|broke production|broken build)\b/i;
|
|
4002
|
+
var WORKAROUND_RE = /\b(workaround|work around|hack(?:y|ish)?|band[- ]?aid|temporary fix|temp fix|quick fix|kludge|monkey[- ]?patch|stop[- ]?gap|FIXME|XXX)\b/i;
|
|
3797
4003
|
function slugify(text) {
|
|
3798
4004
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "reverted-change";
|
|
3799
4005
|
}
|
|
@@ -3811,6 +4017,9 @@ function proposeSeedsFromCommits(commits, limit = 20) {
|
|
|
3811
4017
|
} else if (FIXUP_RE.test(subject) || URGENT_FIX_RE.test(subject)) {
|
|
3812
4018
|
what = subject.replace(FIXUP_RE, "").trim() || subject;
|
|
3813
4019
|
kind = "fixup";
|
|
4020
|
+
} else if (WORKAROUND_RE.test(subject)) {
|
|
4021
|
+
what = subject;
|
|
4022
|
+
kind = "workaround";
|
|
3814
4023
|
}
|
|
3815
4024
|
if (!what || !kind) continue;
|
|
3816
4025
|
const slug = slugify(what);
|
|
@@ -3819,7 +4028,7 @@ function proposeSeedsFromCommits(commits, limit = 20) {
|
|
|
3819
4028
|
out.push({
|
|
3820
4029
|
slug,
|
|
3821
4030
|
what,
|
|
3822
|
-
why_failed: kind === "revert" ? `This change was reverted in commit ${commit.sha} \u2014 it caused a regression and was backed out. Verify the root cause before re-attempting.` : `This area required an urgent fix (commit ${commit.sha}: "${subject}") \u2014 it shipped broken once. Treat changes here with extra care.`,
|
|
4031
|
+
why_failed: kind === "revert" ? `This change was reverted in commit ${commit.sha} \u2014 it caused a regression and was backed out. Verify the root cause before re-attempting.` : kind === "workaround" ? `This area carries a known workaround/stop-gap (commit ${commit.sha}: "${subject}") \u2014 the proper fix is still owed. Understand why the workaround exists before changing it.` : `This area required an urgent fix (commit ${commit.sha}: "${subject}") \u2014 it shipped broken once. Treat changes here with extra care.`,
|
|
3823
4032
|
paths: (commit.files ?? []).slice(0, 8),
|
|
3824
4033
|
source_sha: commit.sha,
|
|
3825
4034
|
kind
|
|
@@ -3829,6 +4038,75 @@ function proposeSeedsFromCommits(commits, limit = 20) {
|
|
|
3829
4038
|
return out;
|
|
3830
4039
|
}
|
|
3831
4040
|
|
|
4041
|
+
// src/seed.ts
|
|
4042
|
+
var JS_DETECTORS = [
|
|
4043
|
+
["nestjs", ["@nestjs/core"]],
|
|
4044
|
+
["nextjs", ["next"]],
|
|
4045
|
+
["remix", ["@remix-run/react", "@remix-run/node"]],
|
|
4046
|
+
["react", ["react"]],
|
|
4047
|
+
["express", ["express"]],
|
|
4048
|
+
["fastify", ["fastify"]],
|
|
4049
|
+
["prisma", ["@prisma/client", "prisma"]],
|
|
4050
|
+
["drizzle", ["drizzle-orm"]],
|
|
4051
|
+
["zustand", ["zustand"]],
|
|
4052
|
+
["redux", ["@reduxjs/toolkit", "redux"]],
|
|
4053
|
+
["reactquery", ["@tanstack/react-query", "react-query"]],
|
|
4054
|
+
["trpc", ["@trpc/server", "@trpc/client"]],
|
|
4055
|
+
["mongoose", ["mongoose"]],
|
|
4056
|
+
["graphql", ["@apollo/client", "@apollo/server", "apollo-server", "graphql"]],
|
|
4057
|
+
["vue", ["vue", "@vue/core"]],
|
|
4058
|
+
["tailwind", ["tailwindcss"]],
|
|
4059
|
+
["vite", ["vite"]],
|
|
4060
|
+
["sveltekit", ["@sveltejs/kit"]],
|
|
4061
|
+
["astro", ["astro"]],
|
|
4062
|
+
["typescript", ["typescript"]],
|
|
4063
|
+
["monorepo", ["turbo", "nx", "@nrwl/workspace", "@nx/workspace"]]
|
|
4064
|
+
];
|
|
4065
|
+
var PYTHON_DETECTORS = [
|
|
4066
|
+
["fastapi", /\bfastapi\b/i],
|
|
4067
|
+
["django", /\bdjango\b/i],
|
|
4068
|
+
["flask", /\bflask\b/i]
|
|
4069
|
+
];
|
|
4070
|
+
function detectFromPackageJson(deps) {
|
|
4071
|
+
const detected = [];
|
|
4072
|
+
for (const [stack, signals] of JS_DETECTORS) {
|
|
4073
|
+
if (signals.some((s) => s in deps)) detected.push(stack);
|
|
4074
|
+
}
|
|
4075
|
+
if (detected.includes("nextjs") || detected.includes("remix")) {
|
|
4076
|
+
return detected.filter((s) => s !== "react");
|
|
4077
|
+
}
|
|
4078
|
+
return detected;
|
|
4079
|
+
}
|
|
4080
|
+
function detectFromRequirementsTxt(content) {
|
|
4081
|
+
return PYTHON_DETECTORS.filter(([, re]) => re.test(content)).map(([s]) => s);
|
|
4082
|
+
}
|
|
4083
|
+
function detectFromGoMod(content) {
|
|
4084
|
+
return /^\s*module\s+\S/m.test(content) ? ["go"] : [];
|
|
4085
|
+
}
|
|
4086
|
+
function detectFromPomXml(content) {
|
|
4087
|
+
return /org\.springframework|spring-boot/.test(content) ? ["spring"] : [];
|
|
4088
|
+
}
|
|
4089
|
+
function detectFromComposerJson(content) {
|
|
4090
|
+
return /laravel\/framework|illuminate\//.test(content) ? ["laravel"] : [];
|
|
4091
|
+
}
|
|
4092
|
+
function detectFromGemfile(content) {
|
|
4093
|
+
return /^\s*gem\s+["']rails["']/m.test(content) || /\brails\b/.test(content) ? ["rails"] : [];
|
|
4094
|
+
}
|
|
4095
|
+
function detectStacksFromManifests(input) {
|
|
4096
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4097
|
+
const add = (stacks) => stacks.forEach((s) => seen.add(s));
|
|
4098
|
+
if (input.packageJsonDeps) add(detectFromPackageJson(input.packageJsonDeps));
|
|
4099
|
+
if (input.requirementsTxt) add(detectFromRequirementsTxt(input.requirementsTxt));
|
|
4100
|
+
if (input.goMod) add(detectFromGoMod(input.goMod));
|
|
4101
|
+
if (input.pomXml) add(detectFromPomXml(input.pomXml));
|
|
4102
|
+
if (input.composerJson) add(detectFromComposerJson(input.composerJson));
|
|
4103
|
+
if (input.gemfile) add(detectFromGemfile(input.gemfile));
|
|
4104
|
+
if (input.hasCsproj) add(["dotnet"]);
|
|
4105
|
+
if (input.hasDockerfile) add(["docker"]);
|
|
4106
|
+
if (input.hasTurboJson || input.hasNxJson) add(["monorepo"]);
|
|
4107
|
+
return Array.from(seen);
|
|
4108
|
+
}
|
|
4109
|
+
|
|
3832
4110
|
// src/merge-memory.ts
|
|
3833
4111
|
function safeParse(raw) {
|
|
3834
4112
|
try {
|
|
@@ -3916,10 +4194,149 @@ function classifyMemoryPriority(signals) {
|
|
|
3916
4194
|
function priorityRank(priority) {
|
|
3917
4195
|
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
3918
4196
|
}
|
|
4197
|
+
|
|
4198
|
+
// src/bridges.ts
|
|
4199
|
+
var BRIDGE_TARGET_PATH = {
|
|
4200
|
+
claude: "CLAUDE.md",
|
|
4201
|
+
cursor: ".cursor/rules/haive-memories.mdc",
|
|
4202
|
+
cline: ".clinerules",
|
|
4203
|
+
windsurf: ".windsurfrules",
|
|
4204
|
+
continue: ".continuerules",
|
|
4205
|
+
cody: ".sourcegraph/cody-rules.md",
|
|
4206
|
+
zed: ".rules",
|
|
4207
|
+
roo: ".roo/rules/haive.md",
|
|
4208
|
+
gemini: "GEMINI.md",
|
|
4209
|
+
aider: "CONVENTIONS.md",
|
|
4210
|
+
agents: "AGENTS.md",
|
|
4211
|
+
copilot: ".github/copilot-instructions.md"
|
|
4212
|
+
};
|
|
4213
|
+
var BRIDGE_TARGETS = Object.keys(BRIDGE_TARGET_PATH);
|
|
4214
|
+
var BRIDGE_MARKERS = {
|
|
4215
|
+
memoriesStart: "<!-- haive:memories-start -->",
|
|
4216
|
+
memoriesEnd: "<!-- haive:memories-end -->",
|
|
4217
|
+
sensorsStart: "<!-- haive:sensors-start -->",
|
|
4218
|
+
sensorsEnd: "<!-- haive:sensors-end -->"
|
|
4219
|
+
};
|
|
4220
|
+
function bridgeMemorySummary(body) {
|
|
4221
|
+
const firstLine = body.split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l.length > 0) ?? "";
|
|
4222
|
+
const oneLine = firstLine.replace(/\s+/g, " ");
|
|
4223
|
+
return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
|
|
4224
|
+
}
|
|
4225
|
+
function prepareBridgeData(memories, sensors, opts) {
|
|
4226
|
+
const max = opts?.maxMemories ?? 8;
|
|
4227
|
+
const topMemories = memories.filter((m) => {
|
|
4228
|
+
const s = m.frontmatter.status;
|
|
4229
|
+
if (m.frontmatter.type === "session_recap") return false;
|
|
4230
|
+
if (m.frontmatter.tags?.includes("stack-pack") || m.frontmatter.tags?.includes("seed")) return false;
|
|
4231
|
+
return s === "validated" || s === "proposed";
|
|
4232
|
+
}).sort((a, b) => {
|
|
4233
|
+
const score = (m) => m.frontmatter.status === "validated" ? 2 : 1;
|
|
4234
|
+
return score(b) - score(a);
|
|
4235
|
+
}).slice(0, max).map((m) => ({
|
|
4236
|
+
id: m.frontmatter.id,
|
|
4237
|
+
scope: m.frontmatter.scope,
|
|
4238
|
+
type: m.frontmatter.type,
|
|
4239
|
+
summary: bridgeMemorySummary(m.body),
|
|
4240
|
+
paths: m.frontmatter.anchor?.paths ?? []
|
|
4241
|
+
}));
|
|
4242
|
+
const blockSensors = sensors.filter((s) => s.severity === "block");
|
|
4243
|
+
return { topMemories, blockSensors };
|
|
4244
|
+
}
|
|
4245
|
+
function renderMemoriesBlock(topMemories) {
|
|
4246
|
+
const lines = [
|
|
4247
|
+
BRIDGE_MARKERS.memoriesStart,
|
|
4248
|
+
"<!-- AUTO-GENERATED by haive bridges sync \u2014 do not edit between these markers -->",
|
|
4249
|
+
"<!-- Top memories \u2014 call get_briefing / mem_get for the full body. -->",
|
|
4250
|
+
""
|
|
4251
|
+
];
|
|
4252
|
+
if (topMemories.length === 0) {
|
|
4253
|
+
lines.push("_(no validated memories yet \u2014 run `haive sync` to populate)_");
|
|
4254
|
+
} else {
|
|
4255
|
+
for (const m of topMemories) {
|
|
4256
|
+
const scopeNote = m.paths.length > 0 ? ` _(applies to: ${m.paths.slice(0, 4).join(", ")}${m.paths.length > 4 ? ", \u2026" : ""})_` : "";
|
|
4257
|
+
lines.push(`- \`${m.id}\` (${m.scope}/${m.type}) \u2014 ${m.summary}${scopeNote}`);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
lines.push("", BRIDGE_MARKERS.memoriesEnd);
|
|
4261
|
+
return lines.join("\n");
|
|
4262
|
+
}
|
|
4263
|
+
function renderSensorsBlock(blockSensors) {
|
|
4264
|
+
if (blockSensors.length === 0) return "";
|
|
4265
|
+
const lines = [
|
|
4266
|
+
BRIDGE_MARKERS.sensorsStart,
|
|
4267
|
+
"<!-- AUTO-GENERATED by haive bridges sync \u2014 do not edit between these markers -->",
|
|
4268
|
+
"",
|
|
4269
|
+
"## Hard rules \u2014 hAIve block sensors",
|
|
4270
|
+
"",
|
|
4271
|
+
"The patterns below are blocked by the repo enforcement gate.",
|
|
4272
|
+
"Introducing them will fail the pre-commit check (`haive enforce check`).",
|
|
4273
|
+
""
|
|
4274
|
+
];
|
|
4275
|
+
for (const s of blockSensors) {
|
|
4276
|
+
const pathNote = s.paths.length > 0 ? ` _(applies to: ${s.paths.join(", ")})_` : "";
|
|
4277
|
+
lines.push(`- **${s.id}**${pathNote}: ${s.message}`);
|
|
4278
|
+
if (s.pattern) lines.push(` - Pattern: \`${s.pattern}\``);
|
|
4279
|
+
}
|
|
4280
|
+
lines.push("", BRIDGE_MARKERS.sensorsEnd);
|
|
4281
|
+
return lines.join("\n");
|
|
4282
|
+
}
|
|
4283
|
+
var HAIVE_PREAMBLE = "This repo uses **[hAIve](https://github.com/Doucs91/hAIve)** for shared context and enforcement.\n\n**Before editing** for a goal: call `get_briefing` (task + files/symbols) to load ranked context.\n**When an approach fails**: call `mem_tried` right away.\n**Before closing**: run `haive enforce finish` and capture learnings via the `post_task` prompt.\n\nIf `get_briefing` returns `action_required`, surface each item to the developer and wait for\nexplicit confirmation before modifying any code.";
|
|
4284
|
+
function renderMarkdownBridge(topMemories, blockSensors, title) {
|
|
4285
|
+
const parts = [
|
|
4286
|
+
`# ${title}`,
|
|
4287
|
+
"",
|
|
4288
|
+
HAIVE_PREAMBLE,
|
|
4289
|
+
"",
|
|
4290
|
+
"## Memories",
|
|
4291
|
+
"",
|
|
4292
|
+
renderMemoriesBlock(topMemories)
|
|
4293
|
+
];
|
|
4294
|
+
const sensorsBlock = renderSensorsBlock(blockSensors);
|
|
4295
|
+
if (sensorsBlock) {
|
|
4296
|
+
parts.push("", sensorsBlock);
|
|
4297
|
+
}
|
|
4298
|
+
return parts.join("\n") + "\n";
|
|
4299
|
+
}
|
|
4300
|
+
function renderCursorBridge(topMemories, blockSensors) {
|
|
4301
|
+
const frontmatter = [
|
|
4302
|
+
"---",
|
|
4303
|
+
"description: hAIve shared memories & block sensors (auto-generated)",
|
|
4304
|
+
"alwaysApply: true",
|
|
4305
|
+
"---",
|
|
4306
|
+
""
|
|
4307
|
+
].join("\n");
|
|
4308
|
+
return frontmatter + renderMarkdownBridge(topMemories, blockSensors, "hAIve rules (Cursor)");
|
|
4309
|
+
}
|
|
4310
|
+
var FORMATTERS = {
|
|
4311
|
+
claude: (m, s) => renderMarkdownBridge(m, s, "CLAUDE.md \u2014 hAIve context"),
|
|
4312
|
+
cursor: (m, s) => renderCursorBridge(m, s),
|
|
4313
|
+
cline: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Cline)"),
|
|
4314
|
+
windsurf: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Windsurf)"),
|
|
4315
|
+
continue: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Continue)"),
|
|
4316
|
+
cody: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Cody / Sourcegraph)"),
|
|
4317
|
+
zed: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Zed)"),
|
|
4318
|
+
roo: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (Roo Code)"),
|
|
4319
|
+
gemini: (m, s) => renderMarkdownBridge(m, s, "GEMINI.md \u2014 hAIve context"),
|
|
4320
|
+
aider: (m, s) => renderMarkdownBridge(m, s, "CONVENTIONS.md \u2014 hAIve context (Aider)"),
|
|
4321
|
+
agents: (m, s) => renderMarkdownBridge(m, s, "AGENTS.md \u2014 hAIve context"),
|
|
4322
|
+
copilot: (m, s) => renderMarkdownBridge(m, s, "hAIve rules (GitHub Copilot)")
|
|
4323
|
+
};
|
|
4324
|
+
function generateBridges(memories, sensors, opts) {
|
|
4325
|
+
const { topMemories, blockSensors } = prepareBridgeData(memories, sensors, opts);
|
|
4326
|
+
const targets = opts?.targets ?? BRIDGE_TARGETS;
|
|
4327
|
+
return targets.map((target) => ({
|
|
4328
|
+
target,
|
|
4329
|
+
path: BRIDGE_TARGET_PATH[target],
|
|
4330
|
+
content: FORMATTERS[target](topMemories, blockSensors)
|
|
4331
|
+
}));
|
|
4332
|
+
}
|
|
3919
4333
|
export {
|
|
3920
4334
|
AUTOPILOT_DEFAULTS,
|
|
3921
4335
|
ActivationSchema,
|
|
3922
4336
|
AnchorSchema,
|
|
4337
|
+
BRIDGE_MARKERS,
|
|
4338
|
+
BRIDGE_TARGETS,
|
|
4339
|
+
BRIDGE_TARGET_PATH,
|
|
3923
4340
|
BRIEFING_MARKER_TTL_MS,
|
|
3924
4341
|
BRIEFING_PRESET_DEFAULTS,
|
|
3925
4342
|
CHARS_PER_TOKEN,
|
|
@@ -3962,8 +4379,11 @@ export {
|
|
|
3962
4379
|
appendPreventionEvent,
|
|
3963
4380
|
appendRuntimeJournalEntry,
|
|
3964
4381
|
appendUsageEvent,
|
|
4382
|
+
applyFeedbackAdjustment,
|
|
4383
|
+
bridgeMemorySummary,
|
|
3965
4384
|
briefingMarkerPath,
|
|
3966
4385
|
briefingMarkersDir,
|
|
4386
|
+
briefingProofLine,
|
|
3967
4387
|
buildCodeMap,
|
|
3968
4388
|
buildCoverageIndex,
|
|
3969
4389
|
buildDashboard,
|
|
@@ -3976,6 +4396,7 @@ export {
|
|
|
3976
4396
|
collectTimelineEntries,
|
|
3977
4397
|
compactAutoRecapBody,
|
|
3978
4398
|
compareEvalReports,
|
|
4399
|
+
compareGatePrecision,
|
|
3979
4400
|
compareImpact,
|
|
3980
4401
|
compileRegexSensor,
|
|
3981
4402
|
computeEvalTrend,
|
|
@@ -3986,6 +4407,7 @@ export {
|
|
|
3986
4407
|
configPath,
|
|
3987
4408
|
contractLockPath,
|
|
3988
4409
|
deriveConfidence,
|
|
4410
|
+
detectStacksFromManifests,
|
|
3989
4411
|
diffContract,
|
|
3990
4412
|
diffHasDistinctiveOverlap,
|
|
3991
4413
|
distinctiveCap,
|
|
@@ -4007,6 +4429,7 @@ export {
|
|
|
4007
4429
|
findingBody,
|
|
4008
4430
|
findingToDraft,
|
|
4009
4431
|
firstMemoryOneLine,
|
|
4432
|
+
generateBridges,
|
|
4010
4433
|
getUsage,
|
|
4011
4434
|
globToRegExp,
|
|
4012
4435
|
hasRecentBriefingMarker,
|
|
@@ -4043,14 +4466,17 @@ export {
|
|
|
4043
4466
|
normalizeFindingSeverity,
|
|
4044
4467
|
normalizeSessionId,
|
|
4045
4468
|
overallScore,
|
|
4469
|
+
parseEslintJson,
|
|
4046
4470
|
parseFindings,
|
|
4047
4471
|
parseMemory,
|
|
4472
|
+
parseNpmAudit,
|
|
4048
4473
|
parseSarif,
|
|
4049
4474
|
parseSince,
|
|
4050
4475
|
parseSonar,
|
|
4051
4476
|
pathsOverlap,
|
|
4052
4477
|
pickSnippetNeedle,
|
|
4053
4478
|
planConflictResolution,
|
|
4479
|
+
prepareBridgeData,
|
|
4054
4480
|
preventionLogPath,
|
|
4055
4481
|
priorityRank,
|
|
4056
4482
|
prioritySignals,
|
|
@@ -4062,11 +4488,13 @@ export {
|
|
|
4062
4488
|
readRecentBriefingMarker,
|
|
4063
4489
|
readRuntimeJournalTail,
|
|
4064
4490
|
readUsageEvents,
|
|
4491
|
+
recommendFeedbackAdjustment,
|
|
4065
4492
|
recordApplied,
|
|
4066
4493
|
recordPrevention,
|
|
4067
4494
|
recordProjectContextEmission,
|
|
4068
4495
|
recordRejection,
|
|
4069
4496
|
relPathFrom,
|
|
4497
|
+
renderCaughtForYou,
|
|
4070
4498
|
resolveBriefingBudget,
|
|
4071
4499
|
resolveHaivePaths,
|
|
4072
4500
|
resolveManifestFiles,
|
|
@@ -4090,6 +4518,7 @@ export {
|
|
|
4090
4518
|
suggestGate,
|
|
4091
4519
|
suggestSensorFromMemory,
|
|
4092
4520
|
suggestTopicKey,
|
|
4521
|
+
summarizeCaughtForYou,
|
|
4093
4522
|
summarizeImpact,
|
|
4094
4523
|
synthesizeSelfEvalCases,
|
|
4095
4524
|
titleFromBody,
|