@hivelore/cli 0.42.1 → 0.44.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/README.md +7 -6
- package/dist/{chunk-EJ7A4IKD.js → chunk-6KRXMDLC.js} +135 -44
- package/dist/chunk-6KRXMDLC.js.map +1 -0
- package/dist/index.js +327 -74
- package/dist/index.js.map +1 -1
- package/dist/{server-PZWIQUU7.js → server-MCNCAFJN.js} +6 -4
- package/package.json +21 -6
- package/dist/chunk-EJ7A4IKD.js.map +0 -1
- /package/dist/{server-PZWIQUU7.js.map → server-MCNCAFJN.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readPresumedCorrectTargets,
|
|
14
14
|
runAstSensorOnContent,
|
|
15
15
|
runHaiveMcpStdio
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-6KRXMDLC.js";
|
|
17
17
|
import {
|
|
18
18
|
registerMemoryPending
|
|
19
19
|
} from "./chunk-OYJKHD22.js";
|
|
@@ -1299,7 +1299,7 @@ function attachSemanticCommands(embeddings, naming) {
|
|
|
1299
1299
|
minScore: Number(opts.minScore ?? 0)
|
|
1300
1300
|
});
|
|
1301
1301
|
if (!result) {
|
|
1302
|
-
ui.error("No embeddings index found. Run `hivelore
|
|
1302
|
+
ui.error("No embeddings index found. Run `hivelore index memories` first.");
|
|
1303
1303
|
process.exitCode = 1;
|
|
1304
1304
|
return;
|
|
1305
1305
|
}
|
|
@@ -1319,7 +1319,7 @@ function attachSemanticCommands(embeddings, naming) {
|
|
|
1319
1319
|
const { indexStat } = await loadEmbeddings();
|
|
1320
1320
|
const stat2 = await indexStat(paths);
|
|
1321
1321
|
if (!stat2.exists) {
|
|
1322
|
-
ui.warn("No embeddings index. Run `hivelore
|
|
1322
|
+
ui.warn("No embeddings index. Run `hivelore index memories` to create one.");
|
|
1323
1323
|
return;
|
|
1324
1324
|
}
|
|
1325
1325
|
console.log(`${ui.bold("entries:")} ${stat2.count}`);
|
|
@@ -1393,7 +1393,7 @@ function registerIndexCode(program2) {
|
|
|
1393
1393
|
mod = await import("@hivelore/embeddings");
|
|
1394
1394
|
} catch {
|
|
1395
1395
|
ui.error(
|
|
1396
|
-
"@hivelore/embeddings is not installed. Install it (`pnpm add @hivelore/embeddings`)
|
|
1396
|
+
"@hivelore/embeddings is not installed. Install it (`pnpm add @hivelore/embeddings`) then run `hivelore index code-search`."
|
|
1397
1397
|
);
|
|
1398
1398
|
process.exit(1);
|
|
1399
1399
|
}
|
|
@@ -3758,7 +3758,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3758
3758
|
|
|
3759
3759
|
// src/commands/init.ts
|
|
3760
3760
|
var execFileAsync = promisify2(execFile2);
|
|
3761
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3761
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.44.0"}`;
|
|
3762
3762
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3763
3763
|
|
|
3764
3764
|
> Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -5259,7 +5259,7 @@ Wait for **explicit confirmation** before acting.
|
|
|
5259
5259
|
)
|
|
5260
5260
|
);
|
|
5261
5261
|
} catch {
|
|
5262
|
-
ui.warn("--embed: @hivelore/embeddings not available or index build failed.
|
|
5262
|
+
ui.warn("--embed: @hivelore/embeddings not available or index build failed. Install it, then run `hivelore index memories`.");
|
|
5263
5263
|
}
|
|
5264
5264
|
}
|
|
5265
5265
|
});
|
|
@@ -6974,6 +6974,7 @@ import {
|
|
|
6974
6974
|
loadConfig as loadConfig6,
|
|
6975
6975
|
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
6976
6976
|
loadPreventionEvents,
|
|
6977
|
+
readUsageEvents,
|
|
6977
6978
|
loadUsageIndex as loadUsageIndex11,
|
|
6978
6979
|
memoryFilePath as memoryFilePath5,
|
|
6979
6980
|
renderCaughtForYou,
|
|
@@ -7226,6 +7227,14 @@ async function observationStart(paths) {
|
|
|
7226
7227
|
}
|
|
7227
7228
|
return first;
|
|
7228
7229
|
}
|
|
7230
|
+
async function manualSessionStart(paths) {
|
|
7231
|
+
const events = await readUsageEvents(paths);
|
|
7232
|
+
for (let index = events.length - 1; index >= 0; index--) {
|
|
7233
|
+
const event = events[index];
|
|
7234
|
+
if (["get_briefing", "briefing", "session_start", "enforce_session_start"].includes(event.tool)) return event.at;
|
|
7235
|
+
}
|
|
7236
|
+
return null;
|
|
7237
|
+
}
|
|
7229
7238
|
async function printCaughtForYou(paths, since, quiet) {
|
|
7230
7239
|
if (quiet) return;
|
|
7231
7240
|
const memories = existsSync32(paths.memoriesDir) ? await loadMemoriesFromDir10(paths.memoriesDir) : [];
|
|
@@ -7275,7 +7284,7 @@ function registerSessionEnd(session2) {
|
|
|
7275
7284
|
let resolvedFiles = opts.files;
|
|
7276
7285
|
let goal = opts.goal;
|
|
7277
7286
|
let accomplished = opts.accomplished ?? opts.summary;
|
|
7278
|
-
const caughtSince = opts.auto ? await observationStart(paths) :
|
|
7287
|
+
const caughtSince = opts.auto ? await observationStart(paths) : await manualSessionStart(paths);
|
|
7279
7288
|
if (opts.auto) {
|
|
7280
7289
|
const autoCaptured = await autoCaptureFailureLessons(paths, root, await readObservationList(paths)).catch(() => ({ written: 0, ids: [] }));
|
|
7281
7290
|
const synth = await buildAutoRecap(paths);
|
|
@@ -7416,7 +7425,7 @@ import {
|
|
|
7416
7425
|
loadUsageIndex as loadUsageIndex12,
|
|
7417
7426
|
loadPreventionEvents as loadPreventionEvents2,
|
|
7418
7427
|
parseSince,
|
|
7419
|
-
readUsageEvents,
|
|
7428
|
+
readUsageEvents as readUsageEvents2,
|
|
7420
7429
|
renderPreventionReceiptShare,
|
|
7421
7430
|
resolveHaivePaths as resolveHaivePaths27,
|
|
7422
7431
|
usageLogSize
|
|
@@ -7465,7 +7474,7 @@ function registerStats(program2) {
|
|
|
7465
7474
|
);
|
|
7466
7475
|
return;
|
|
7467
7476
|
}
|
|
7468
|
-
const events = await
|
|
7477
|
+
const events = await readUsageEvents2(paths);
|
|
7469
7478
|
const since = parseSince(opts.since);
|
|
7470
7479
|
const aggregate = aggregateUsage(events, since ?? void 0);
|
|
7471
7480
|
if (opts.json) {
|
|
@@ -7501,7 +7510,7 @@ function registerStats(program2) {
|
|
|
7501
7510
|
async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
7502
7511
|
const outAbs = path30.isAbsolute(outRelative) ? path30.resolve(outRelative) : path30.resolve(root, outRelative);
|
|
7503
7512
|
const size = await usageLogSize(paths);
|
|
7504
|
-
let events = await
|
|
7513
|
+
let events = await readUsageEvents2(paths);
|
|
7505
7514
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
7506
7515
|
if (existsSync33(paths.memoriesDir)) {
|
|
7507
7516
|
const mems = await loadMemoriesFromDir11(paths.memoriesDir);
|
|
@@ -7666,7 +7675,7 @@ function registerBench(program2) {
|
|
|
7666
7675
|
},
|
|
7667
7676
|
async () => {
|
|
7668
7677
|
const t0 = performance.now();
|
|
7669
|
-
const out = await antiPatternsCheck({ diff: task, paths: [], limit: 5, semantic: true }, ctx);
|
|
7678
|
+
const out = await antiPatternsCheck({ diff: task, paths: [], limit: 5, semantic: true, track: false }, ctx);
|
|
7670
7679
|
return summarize("anti_patterns_check", t0, out, [
|
|
7671
7680
|
`${out.warnings.length}/${out.scanned} warn`
|
|
7672
7681
|
]);
|
|
@@ -7757,7 +7766,9 @@ function registerBenchmark(program2) {
|
|
|
7757
7766
|
" - Hivelore agents must run `hivelore briefing --files ... --task ...` first.",
|
|
7758
7767
|
" - Plain agents must not read `.ai` or call Hivelore.",
|
|
7759
7768
|
"5. Require every agent to write `BENCHMARK_AGENT_REPORT.md`.",
|
|
7769
|
+
" Its `## Outcome` section must include: Task completed, Tests passed, Policy violations, Duration seconds, Total tokens, Runner ID, Evaluator ID, Independent evaluation.",
|
|
7760
7770
|
"6. Run `hivelore benchmark report --dir <benchmark-root> --out RESULTS.md`.",
|
|
7771
|
+
"7. Do not make comparative claims until evidence_grade=decision-ready (>=10 paired tasks with complete outcomes).",
|
|
7761
7772
|
"",
|
|
7762
7773
|
"Recommended metrics: pass rate, test iterations, files read, files changed, visible artifacts, decision quality, and token proxy."
|
|
7763
7774
|
].join("\n"));
|
|
@@ -7795,15 +7806,35 @@ function parseAgentReport(fixture, report) {
|
|
|
7795
7806
|
terminal_failures: countMatches(section(report, "Terminal Errors"), /fail|error|not raised|exited with code 1/gi),
|
|
7796
7807
|
decision_mentions: sectionBulletCount(report, "Key Decisions"),
|
|
7797
7808
|
report_tokens_est: estimateTokens2(report),
|
|
7798
|
-
haive_impact: /Hivelore Memory Impact[\s\S]*?\b(yes|directly|changed|shaped|confirmed)\b/i.test(report)
|
|
7809
|
+
haive_impact: /Hivelore Memory Impact[\s\S]*?\b(yes|directly|changed|shaped|confirmed)\b/i.test(report),
|
|
7810
|
+
task_completed: reportBoolean(report, "Task completed"),
|
|
7811
|
+
tests_passed: reportBoolean(report, "Tests passed"),
|
|
7812
|
+
policy_violations: reportNumber(report, "Policy violations"),
|
|
7813
|
+
duration_seconds: reportNumber(report, "Duration seconds"),
|
|
7814
|
+
total_tokens: reportNumber(report, "Total tokens"),
|
|
7815
|
+
runner_id: reportValue(report, "Runner ID"),
|
|
7816
|
+
evaluator_id: reportValue(report, "Evaluator ID"),
|
|
7817
|
+
independent_evaluation: reportBoolean(report, "Independent evaluation")
|
|
7799
7818
|
};
|
|
7800
7819
|
}
|
|
7801
7820
|
function summarizeRows(rows) {
|
|
7802
7821
|
const byGroup = (group) => rows.filter((r) => r.group === group);
|
|
7822
|
+
const haiveRows = byGroup("haive");
|
|
7823
|
+
const plainRows = byGroup("plain");
|
|
7824
|
+
const taskName = (fixture) => fixture.replace(/-(haive|plain)$/, "");
|
|
7825
|
+
const plainTasks = new Set(plainRows.map((row) => taskName(row.fixture)));
|
|
7826
|
+
const pairedTasks = new Set(haiveRows.map((row) => taskName(row.fixture)).filter((name) => plainTasks.has(name))).size;
|
|
7827
|
+
const outcomeComplete = rows.length > 0 && rows.every(
|
|
7828
|
+
(row) => row.task_completed !== null && row.tests_passed !== null && row.policy_violations !== null && row.duration_seconds !== null && row.total_tokens !== null && row.runner_id !== null && row.evaluator_id !== null && row.evaluator_id !== row.runner_id && row.independent_evaluation === true
|
|
7829
|
+
);
|
|
7830
|
+
const decisionReady = pairedTasks >= 10 && outcomeComplete;
|
|
7803
7831
|
return {
|
|
7804
7832
|
fixtures: rows.length,
|
|
7805
|
-
|
|
7806
|
-
|
|
7833
|
+
paired_tasks: pairedTasks,
|
|
7834
|
+
evidence_grade: decisionReady ? "decision-ready" : "insufficient",
|
|
7835
|
+
evidence_reason: decisionReady ? "At least 10 paired tasks with complete outcomes reviewed by an evaluator distinct from the runner." : `Need >=10 paired tasks, complete Outcome fields, and independent evaluator attestations; found ${pairedTasks} pair(s), outcome_complete=${outcomeComplete}.`,
|
|
7836
|
+
haive: summarizeGroup(haiveRows),
|
|
7837
|
+
plain: summarizeGroup(plainRows)
|
|
7807
7838
|
};
|
|
7808
7839
|
}
|
|
7809
7840
|
function summarizeGroup(rows) {
|
|
@@ -7817,7 +7848,12 @@ function summarizeGroup(rows) {
|
|
|
7817
7848
|
terminal_failures: sum("terminal_failures"),
|
|
7818
7849
|
decision_mentions: sum("decision_mentions"),
|
|
7819
7850
|
report_tokens_est: sum("report_tokens_est"),
|
|
7820
|
-
haive_impact_count: rows.filter((r) => r.haive_impact).length
|
|
7851
|
+
haive_impact_count: rows.filter((r) => r.haive_impact).length,
|
|
7852
|
+
completed: rows.filter((r) => r.task_completed === true).length,
|
|
7853
|
+
tests_passed: rows.filter((r) => r.tests_passed === true).length,
|
|
7854
|
+
policy_violations: rows.reduce((total, row) => total + (row.policy_violations ?? 0), 0),
|
|
7855
|
+
duration_seconds: rows.reduce((total, row) => total + (row.duration_seconds ?? 0), 0),
|
|
7856
|
+
total_tokens: rows.reduce((total, row) => total + (row.total_tokens ?? 0), 0)
|
|
7821
7857
|
};
|
|
7822
7858
|
}
|
|
7823
7859
|
function renderMarkdown(root, summary, rows) {
|
|
@@ -7828,6 +7864,8 @@ function renderMarkdown(root, summary, rows) {
|
|
|
7828
7864
|
"",
|
|
7829
7865
|
"## Summary",
|
|
7830
7866
|
"",
|
|
7867
|
+
`Evidence grade: **${summary.evidence_grade}** \u2014 ${summary.evidence_reason}`,
|
|
7868
|
+
"",
|
|
7831
7869
|
"| Group | Fixtures | Commands | Files read | Files modified | Test iterations | Terminal failures | Decision mentions | Report tokens (est, report only) | Hivelore impact |",
|
|
7832
7870
|
"| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
|
|
7833
7871
|
groupLine("Hivelore", summary.haive),
|
|
@@ -7851,6 +7889,23 @@ function renderMarkdown(root, summary, rows) {
|
|
|
7851
7889
|
];
|
|
7852
7890
|
return lines.join("\n");
|
|
7853
7891
|
}
|
|
7892
|
+
function reportValue(report, label) {
|
|
7893
|
+
const match = new RegExp(`^[-*]\\s*${escapeRegExp(label)}\\s*:\\s*(.+)$`, "im").exec(report);
|
|
7894
|
+
return match?.[1]?.trim() ?? null;
|
|
7895
|
+
}
|
|
7896
|
+
function reportBoolean(report, label) {
|
|
7897
|
+
const value = reportValue(report, label);
|
|
7898
|
+
if (!value) return null;
|
|
7899
|
+
if (/^(yes|true|pass|passed|complete|completed)$/i.test(value)) return true;
|
|
7900
|
+
if (/^(no|false|fail|failed|incomplete)$/i.test(value)) return false;
|
|
7901
|
+
return null;
|
|
7902
|
+
}
|
|
7903
|
+
function reportNumber(report, label) {
|
|
7904
|
+
const value = reportValue(report, label);
|
|
7905
|
+
if (!value) return null;
|
|
7906
|
+
const parsed = Number(value.replace(/,/g, ""));
|
|
7907
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
7908
|
+
}
|
|
7854
7909
|
function groupLine(label, group) {
|
|
7855
7910
|
return `| ${label} | ${group.fixtures} | ${group.commands} | ${group.files_read} | ${group.files_modified} | ${group.test_iterations} | ${group.terminal_failures} | ${group.decision_mentions} | ${group.report_tokens_est} | ${group.haive_impact_count} |`;
|
|
7856
7911
|
}
|
|
@@ -7898,7 +7953,7 @@ import {
|
|
|
7898
7953
|
function registerEval(program2) {
|
|
7899
7954
|
program2.command("eval").description(
|
|
7900
7955
|
"Rigorous, repeatable quality eval: do the right memories surface (retrieval) and do the right sensors fire (catch-rate)? Emits a numeric 0\u2013100 score. Uses .ai/eval cases via --spec, or auto-synthesizes cases from anchored memories."
|
|
7901
|
-
).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--fail-under-catch-rate <pct>", "exit non-zero if sensor catch-rate is below this percentage").option("--fail-under-gate-precision <pct>", "exit non-zero if gate precision is below this percentage").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("--regression-gate", "CI-safe gate: compare against the baseline IF one exists (fail on regression), else no-op", false).option("--approve-cases", "approve every proposed golden case (gate-miss labeled) into the scored retrieval set, then exit", false).option("--record", "append this run's score to .ai/.cache/eval-history.jsonl (trend the harness over time)", false).option("--trend", "print the recorded score trend (sparkline + latest/best/delta) and exit", false).option("--ref <ref>", "version/commit label stored with a --record run").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7956
|
+
).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("--semantic-ranking", "run the real embeddings-backed ranking lane; fails if embeddings/index are unavailable", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--fail-under-catch-rate <pct>", "exit non-zero if sensor catch-rate is below this percentage").option("--fail-under-gate-precision <pct>", "exit non-zero if gate precision is below this percentage").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("--regression-gate", "CI-safe gate: compare against the baseline IF one exists (fail on regression), else no-op", false).option("--approve-cases", "approve every proposed golden case (gate-miss labeled) into the scored retrieval set, then exit", false).option("--record", "append this run's score to .ai/.cache/eval-history.jsonl (trend the harness over time)", false).option("--trend", "print the recorded score trend (sparkline + latest/best/delta) and exit", false).option("--ref <ref>", "version/commit label stored with a --record run").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7902
7957
|
const root = findProjectRoot31(opts.dir);
|
|
7903
7958
|
const paths = resolveHaivePaths29(root);
|
|
7904
7959
|
if (!existsSync35(paths.memoriesDir)) {
|
|
@@ -7960,8 +8015,15 @@ function registerEval(program2) {
|
|
|
7960
8015
|
if (spec.retrieval && spec.retrieval.length > 0) {
|
|
7961
8016
|
const results = [];
|
|
7962
8017
|
for (const c of spec.retrieval) {
|
|
7963
|
-
const
|
|
7964
|
-
|
|
8018
|
+
const retrieval = await runRetrieval(c, k, ctx, opts.semanticRanking === true);
|
|
8019
|
+
if (opts.semanticRanking && retrieval.searchMode !== "semantic") {
|
|
8020
|
+
ui.error(
|
|
8021
|
+
"Semantic eval requested, but the embeddings package/index is unavailable. Install @hivelore/embeddings and run `hivelore index memories` first."
|
|
8022
|
+
);
|
|
8023
|
+
process.exitCode = 1;
|
|
8024
|
+
return;
|
|
8025
|
+
}
|
|
8026
|
+
results.push(scoreRetrievalCase(c.name, c.expect_ids, retrieval.ids));
|
|
7965
8027
|
}
|
|
7966
8028
|
retrievalAgg = aggregateRetrieval(results);
|
|
7967
8029
|
}
|
|
@@ -7978,6 +8040,8 @@ function registerEval(program2) {
|
|
|
7978
8040
|
const authoredRetrievalCases = retrievalAgg ? retrievalAgg.cases.slice(resolvedSpec.synthesized) : [];
|
|
7979
8041
|
const authoredRetrievalAgg = authoredRetrievalCases.length > 0 ? aggregateRetrieval(authoredRetrievalCases) : null;
|
|
7980
8042
|
const authoredScore = resolvedSpec.authored > 0 && resolvedSpec.synthesized > 0 && (authoredRetrievalAgg || sensorAgg) ? overallScore(authoredRetrievalAgg, sensorAgg) : null;
|
|
8043
|
+
const authoredReport = resolvedSpec.authored > 0 && (authoredRetrievalAgg || sensorAgg) ? buildReport(authoredRetrievalAgg, sensorAgg) : null;
|
|
8044
|
+
const gateReport = authoredReport ?? report;
|
|
7981
8045
|
const [usage, preventionEvents, config] = await Promise.all([
|
|
7982
8046
|
loadUsageIndex13(paths),
|
|
7983
8047
|
loadPreventionEvents3(paths),
|
|
@@ -7999,13 +8063,14 @@ function registerEval(program2) {
|
|
|
7999
8063
|
});
|
|
8000
8064
|
if (!opts.json) ui.success(`Recorded eval score ${report.score}/100 to history.`);
|
|
8001
8065
|
}
|
|
8002
|
-
const baselineFile = opts.baselineFile ? path32.isAbsolute(opts.baselineFile) ? opts.baselineFile : path32.join(root, opts.baselineFile) : path32.join(root, ".ai", "eval", "baseline.json");
|
|
8066
|
+
const baselineFile = opts.baselineFile ? path32.isAbsolute(opts.baselineFile) ? opts.baselineFile : path32.join(root, opts.baselineFile) : path32.join(root, ".ai", "eval", opts.semanticRanking ? "semantic-baseline.json" : "baseline.json");
|
|
8003
8067
|
if (opts.baseline) {
|
|
8004
8068
|
const snapshot = {
|
|
8005
8069
|
saved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8006
8070
|
k,
|
|
8007
8071
|
spec_source: resolvedSpec.source,
|
|
8008
8072
|
report,
|
|
8073
|
+
...authoredReport ? { authored_report: authoredReport } : {},
|
|
8009
8074
|
gate_precision: gatePrecision
|
|
8010
8075
|
};
|
|
8011
8076
|
await mkdir13(path32.dirname(baselineFile), { recursive: true });
|
|
@@ -8025,7 +8090,7 @@ function registerEval(program2) {
|
|
|
8025
8090
|
}
|
|
8026
8091
|
} else {
|
|
8027
8092
|
const snapshot = JSON.parse(await readFile15(baselineFile, "utf8"));
|
|
8028
|
-
delta = compareEvalReports(snapshot.report,
|
|
8093
|
+
delta = compareEvalReports(snapshot.authored_report ?? snapshot.report, gateReport);
|
|
8029
8094
|
if (snapshot.gate_precision) {
|
|
8030
8095
|
gateDelta = compareGatePrecision(snapshot.gate_precision, gatePrecision);
|
|
8031
8096
|
}
|
|
@@ -8035,6 +8100,7 @@ function registerEval(program2) {
|
|
|
8035
8100
|
console.log(JSON.stringify({
|
|
8036
8101
|
root,
|
|
8037
8102
|
k,
|
|
8103
|
+
ranking_mode: opts.semanticRanking ? "semantic" : "deterministic",
|
|
8038
8104
|
spec_source: resolvedSpec.source,
|
|
8039
8105
|
provenance: {
|
|
8040
8106
|
synthesized: resolvedSpec.synthesized,
|
|
@@ -8042,6 +8108,7 @@ function registerEval(program2) {
|
|
|
8042
8108
|
...authoredScore !== null ? { authored_score: authoredScore } : {}
|
|
8043
8109
|
},
|
|
8044
8110
|
report,
|
|
8111
|
+
...authoredReport ? { authored_report: authoredReport } : {},
|
|
8045
8112
|
tier_contract: { checks: tierChecks, failures: tierFailures.length },
|
|
8046
8113
|
proposed_golden_cases: proposedGoldenCount,
|
|
8047
8114
|
gate_precision: gatePrecision,
|
|
@@ -8049,7 +8116,7 @@ function registerEval(program2) {
|
|
|
8049
8116
|
...gateDelta ? { gate_delta: gateDelta } : {}
|
|
8050
8117
|
}, null, 2));
|
|
8051
8118
|
if (tierFailures.length > 0) process.exitCode = 1;
|
|
8052
|
-
applyExitGates(opts,
|
|
8119
|
+
applyExitGates(opts, gateReport, delta, gatePrecision, gateDelta);
|
|
8053
8120
|
return;
|
|
8054
8121
|
}
|
|
8055
8122
|
if (resolvedSpec.authored === 0 && resolvedSpec.synthesized > 0) {
|
|
@@ -8083,7 +8150,7 @@ function registerEval(program2) {
|
|
|
8083
8150
|
console.log(md);
|
|
8084
8151
|
}
|
|
8085
8152
|
if (tierFailures.length > 0) process.exitCode = 1;
|
|
8086
|
-
applyExitGates(opts,
|
|
8153
|
+
applyExitGates(opts, gateReport, delta, gatePrecision, gateDelta);
|
|
8087
8154
|
});
|
|
8088
8155
|
}
|
|
8089
8156
|
function parsePctThreshold(label, raw) {
|
|
@@ -8172,7 +8239,7 @@ async function resolveSpec(opts, root, memoriesDir) {
|
|
|
8172
8239
|
if (existsSync35(defaultSpec)) {
|
|
8173
8240
|
const raw = await readFile15(defaultSpec, "utf8");
|
|
8174
8241
|
const explicit = JSON.parse(raw);
|
|
8175
|
-
const memories2 = await loadMemoriesFromDir(memoriesDir);
|
|
8242
|
+
const memories2 = (await loadMemoriesFromDir(memoriesDir)).filter(({ memory: memory2 }) => memory2.frontmatter.scope !== "personal");
|
|
8176
8243
|
const synthesized2 = synthesizeSelfEvalCases(memories2, { includeFiles: !opts.semanticOnly });
|
|
8177
8244
|
return {
|
|
8178
8245
|
spec: {
|
|
@@ -8184,7 +8251,7 @@ async function resolveSpec(opts, root, memoriesDir) {
|
|
|
8184
8251
|
authored: countCases(explicit)
|
|
8185
8252
|
};
|
|
8186
8253
|
}
|
|
8187
|
-
const memories = await loadMemoriesFromDir(memoriesDir);
|
|
8254
|
+
const memories = (await loadMemoriesFromDir(memoriesDir)).filter(({ memory: memory2 }) => memory2.frontmatter.scope !== "personal");
|
|
8188
8255
|
const synthesized = synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly });
|
|
8189
8256
|
return {
|
|
8190
8257
|
spec: { retrieval: synthesized },
|
|
@@ -8193,7 +8260,7 @@ async function resolveSpec(opts, root, memoriesDir) {
|
|
|
8193
8260
|
authored: 0
|
|
8194
8261
|
};
|
|
8195
8262
|
}
|
|
8196
|
-
async function runRetrieval(c, k, ctx) {
|
|
8263
|
+
async function runRetrieval(c, k, ctx, semanticRanking = false) {
|
|
8197
8264
|
const out = await getBriefing(
|
|
8198
8265
|
{
|
|
8199
8266
|
task: c.task,
|
|
@@ -8206,16 +8273,18 @@ async function runRetrieval(c, k, ctx) {
|
|
|
8206
8273
|
semantic: true,
|
|
8207
8274
|
include_stale: false,
|
|
8208
8275
|
track: false,
|
|
8276
|
+
memory_scopes: ["team", "module"],
|
|
8277
|
+
deterministic: !semanticRanking,
|
|
8209
8278
|
format: "compact",
|
|
8210
8279
|
min_semantic_score: 0
|
|
8211
8280
|
},
|
|
8212
8281
|
ctx
|
|
8213
8282
|
);
|
|
8214
|
-
return out.memories.map((m) => m.id);
|
|
8283
|
+
return { ids: out.memories.map((m) => m.id), searchMode: out.search_mode };
|
|
8215
8284
|
}
|
|
8216
8285
|
async function runSensorCase(c, ctx) {
|
|
8217
8286
|
const out = await antiPatternsCheck(
|
|
8218
|
-
{ diff: c.diff, paths: c.paths ?? [], limit: 50, semantic: false },
|
|
8287
|
+
{ diff: c.diff, paths: c.paths ?? [], limit: 50, semantic: false, track: false },
|
|
8219
8288
|
ctx
|
|
8220
8289
|
);
|
|
8221
8290
|
return out.warnings.filter((w) => w.reasons.includes("sensor")).map((w) => w.id);
|
|
@@ -8308,7 +8377,7 @@ import {
|
|
|
8308
8377
|
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
8309
8378
|
memoryFilePath as memoryFilePath6,
|
|
8310
8379
|
parseSince as parseSince2,
|
|
8311
|
-
readUsageEvents as
|
|
8380
|
+
readUsageEvents as readUsageEvents3,
|
|
8312
8381
|
resolveHaivePaths as resolveHaivePaths30,
|
|
8313
8382
|
serializeMemory as serializeMemory13,
|
|
8314
8383
|
suggestTopicKey
|
|
@@ -8339,7 +8408,7 @@ function registerMemorySuggest(memory2) {
|
|
|
8339
8408
|
}
|
|
8340
8409
|
const root = findProjectRoot32(opts.dir);
|
|
8341
8410
|
const paths = resolveHaivePaths30(root);
|
|
8342
|
-
const events = await
|
|
8411
|
+
const events = await readUsageEvents3(paths);
|
|
8343
8412
|
if (events.length === 0) {
|
|
8344
8413
|
if (opts.json) {
|
|
8345
8414
|
console.log(JSON.stringify({ suggestions: [] }));
|
|
@@ -8729,7 +8798,7 @@ import {
|
|
|
8729
8798
|
loadMemoriesFromDirDetailed,
|
|
8730
8799
|
loadSensorLedger as loadSensorLedger2,
|
|
8731
8800
|
loadUsageIndex as loadUsageIndex15,
|
|
8732
|
-
readUsageEvents as
|
|
8801
|
+
readUsageEvents as readUsageEvents4,
|
|
8733
8802
|
resolveHaivePaths as resolveHaivePaths33
|
|
8734
8803
|
} from "@hivelore/core";
|
|
8735
8804
|
|
|
@@ -8978,14 +9047,15 @@ function registerDoctor(program2) {
|
|
|
8978
9047
|
}
|
|
8979
9048
|
}
|
|
8980
9049
|
const lintReport = await lintMemoriesAsync(root);
|
|
8981
|
-
|
|
9050
|
+
const actionableLintFindings = lintReport.findings.filter((finding) => finding.severity !== "info");
|
|
9051
|
+
if (actionableLintFindings.length > 0) {
|
|
8982
9052
|
const warnCount = lintReport.findings.filter((finding) => finding.severity === "warn").length;
|
|
8983
9053
|
const errorCount = lintReport.findings.filter((finding) => finding.severity === "error").length;
|
|
8984
9054
|
const severity = errorCount > 0 ? "error" : warnCount > 0 ? "warn" : "info";
|
|
8985
9055
|
findings.push({
|
|
8986
9056
|
severity,
|
|
8987
9057
|
code: "memory-lint-findings",
|
|
8988
|
-
message: `memory lint reports ${
|
|
9058
|
+
message: `memory lint reports ${actionableLintFindings.length} actionable finding${actionableLintFindings.length === 1 ? "" : "s"} (${errorCount} error, ${warnCount} warn).`,
|
|
8989
9059
|
fix: "hivelore memory lint --fix --apply"
|
|
8990
9060
|
});
|
|
8991
9061
|
}
|
|
@@ -9026,9 +9096,35 @@ function registerDoctor(program2) {
|
|
|
9026
9096
|
fix: "hivelore sensors list # then `hivelore sensors promote <id>` for a trusted, non-brittle sensor \u2014 or retire the noise"
|
|
9027
9097
|
});
|
|
9028
9098
|
}
|
|
9099
|
+
const commandSensorMemories = sensorMemories.filter((m) => {
|
|
9100
|
+
const kind = m.memory.frontmatter.sensor?.kind;
|
|
9101
|
+
return kind === "shell" || kind === "test";
|
|
9102
|
+
});
|
|
9103
|
+
if (commandSensorMemories.length > 0 && config.enforcement?.runCommandSensors !== true) {
|
|
9104
|
+
findings.push({
|
|
9105
|
+
severity: "error",
|
|
9106
|
+
code: "command-sensors-disabled",
|
|
9107
|
+
section: "Protection",
|
|
9108
|
+
message: `${commandSensorMemories.length} command/test sensor(s) exist but enforcement.runCommandSensors is not true \u2014 their behaviour protection is OFF.`,
|
|
9109
|
+
fix: "Set enforcement.runCommandSensors=true after reviewing the repo-authored commands."
|
|
9110
|
+
});
|
|
9111
|
+
}
|
|
9112
|
+
const unprovenBlocks = commandSensorMemories.filter((m) => {
|
|
9113
|
+
const sensor = m.memory.frontmatter.sensor;
|
|
9114
|
+
return sensor.severity === "block" && sensor.red_proven !== true;
|
|
9115
|
+
});
|
|
9116
|
+
if (unprovenBlocks.length > 0) {
|
|
9117
|
+
findings.push({
|
|
9118
|
+
severity: "error",
|
|
9119
|
+
code: "command-sensors-block-without-red",
|
|
9120
|
+
section: "Protection",
|
|
9121
|
+
message: `${unprovenBlocks.length} blocking command/test sensor(s) predate mandatory prove-RED and cannot substantiate their incident claim.`,
|
|
9122
|
+
fix: "Re-propose each sensor with --red-ref <pre-fix-commit>, or demote it to warn."
|
|
9123
|
+
});
|
|
9124
|
+
}
|
|
9029
9125
|
const astSensorCount = sensorMemories.filter((m) => m.memory.frontmatter.sensor?.kind === "ast").length;
|
|
9030
9126
|
if (astSensorCount > 0) {
|
|
9031
|
-
const { astEngineAvailable: astEngineAvailable2 } = await import("./server-
|
|
9127
|
+
const { astEngineAvailable: astEngineAvailable2 } = await import("./server-MCNCAFJN.js");
|
|
9032
9128
|
if (!await astEngineAvailable2()) {
|
|
9033
9129
|
findings.push({
|
|
9034
9130
|
severity: "warn",
|
|
@@ -9111,7 +9207,7 @@ function registerDoctor(program2) {
|
|
|
9111
9207
|
});
|
|
9112
9208
|
}
|
|
9113
9209
|
const onDisk = await countSourceFilesOnDisk(root);
|
|
9114
|
-
if (
|
|
9210
|
+
if (isCodeMapNearEmpty(indexedCount, onDisk)) {
|
|
9115
9211
|
findings.push({
|
|
9116
9212
|
severity: "warn",
|
|
9117
9213
|
code: "code-map-near-empty",
|
|
@@ -9134,7 +9230,7 @@ function registerDoctor(program2) {
|
|
|
9134
9230
|
}
|
|
9135
9231
|
} catch {
|
|
9136
9232
|
}
|
|
9137
|
-
const events = await
|
|
9233
|
+
const events = await readUsageEvents4(paths);
|
|
9138
9234
|
if (events.length === 0) {
|
|
9139
9235
|
findings.push({
|
|
9140
9236
|
severity: "info",
|
|
@@ -9198,7 +9294,8 @@ function registerDoctor(program2) {
|
|
|
9198
9294
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
|
|
9199
9295
|
});
|
|
9200
9296
|
}
|
|
9201
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
9297
|
+
findings.push(...await collectInstallFindings(root, "0.44.0"));
|
|
9298
|
+
findings.push(...await collectMcpRuntimeFindings(paths, "0.44.0"));
|
|
9202
9299
|
findings.push(...await collectToolchainFindings(root));
|
|
9203
9300
|
try {
|
|
9204
9301
|
const legacyRaw = execSync("haive-mcp --version", {
|
|
@@ -9206,7 +9303,7 @@ function registerDoctor(program2) {
|
|
|
9206
9303
|
timeout: 3e3,
|
|
9207
9304
|
stdio: ["ignore", "pipe", "ignore"]
|
|
9208
9305
|
}).trim();
|
|
9209
|
-
const cliVersion = "0.
|
|
9306
|
+
const cliVersion = "0.44.0";
|
|
9210
9307
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
9211
9308
|
findings.push({
|
|
9212
9309
|
severity: "warn",
|
|
@@ -9262,6 +9359,39 @@ npm uninstall -g @hivelore/mcp`
|
|
|
9262
9359
|
emit(findings, opts, repairs);
|
|
9263
9360
|
});
|
|
9264
9361
|
}
|
|
9362
|
+
function isCodeMapNearEmpty(indexedCount, sourceFilesOnDisk) {
|
|
9363
|
+
return sourceFilesOnDisk >= 20 && indexedCount < sourceFilesOnDisk * 0.05;
|
|
9364
|
+
}
|
|
9365
|
+
function mcpRuntimeVersionFinding(marker, expectedVersion, processAlive) {
|
|
9366
|
+
if (!processAlive || !marker.version || marker.version === expectedVersion) return null;
|
|
9367
|
+
return {
|
|
9368
|
+
severity: "warn",
|
|
9369
|
+
code: "active-mcp-server-stale",
|
|
9370
|
+
message: `An active MCP server is still v${marker.version} while the installed CLI is v${expectedVersion}. MCP clients keep server processes alive across package upgrades.`,
|
|
9371
|
+
fix: "Restart the IDE/agent MCP client, then confirm get_briefing.server_version matches `hivelore --version`.",
|
|
9372
|
+
section: "Agent coverage"
|
|
9373
|
+
};
|
|
9374
|
+
}
|
|
9375
|
+
async function collectMcpRuntimeFindings(paths, expectedVersion) {
|
|
9376
|
+
const markerFile = path37.join(paths.runtimeDir, "mcp-server.json");
|
|
9377
|
+
if (!existsSync40(markerFile)) return [];
|
|
9378
|
+
try {
|
|
9379
|
+
const marker = JSON.parse(await readFile16(markerFile, "utf8"));
|
|
9380
|
+
let alive = false;
|
|
9381
|
+
if (typeof marker.pid === "number") {
|
|
9382
|
+
try {
|
|
9383
|
+
process.kill(marker.pid, 0);
|
|
9384
|
+
alive = true;
|
|
9385
|
+
} catch {
|
|
9386
|
+
alive = false;
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
const finding = mcpRuntimeVersionFinding(marker, expectedVersion, alive);
|
|
9390
|
+
return finding ? [finding] : [];
|
|
9391
|
+
} catch {
|
|
9392
|
+
return [];
|
|
9393
|
+
}
|
|
9394
|
+
}
|
|
9265
9395
|
function emit(findings, opts, repairs = []) {
|
|
9266
9396
|
const classified = findings.map((finding) => ({
|
|
9267
9397
|
...finding,
|
|
@@ -9441,7 +9571,7 @@ async function collectSemanticIndexFindings(paths, config, memoryCount, codeMap)
|
|
|
9441
9571
|
severity: autoWantsCodeSearch ? "warn" : "info",
|
|
9442
9572
|
code: "embeddings-unavailable",
|
|
9443
9573
|
message: "@hivelore/embeddings is not available, so get_briefing falls back to lexical ranking and code_search cannot run.",
|
|
9444
|
-
fix: "npm install -g @hivelore/
|
|
9574
|
+
fix: "npm install -g @hivelore/embeddings@latest\nhivelore index status",
|
|
9445
9575
|
section: "Index health"
|
|
9446
9576
|
});
|
|
9447
9577
|
return findings;
|
|
@@ -9453,7 +9583,7 @@ async function collectSemanticIndexFindings(paths, config, memoryCount, codeMap)
|
|
|
9453
9583
|
severity: "warn",
|
|
9454
9584
|
code: "semantic-memory-index-missing",
|
|
9455
9585
|
message: "Memory embeddings index is missing or empty; get_briefing will report literal_fallback instead of semantic ranking.",
|
|
9456
|
-
fix: "hivelore
|
|
9586
|
+
fix: "hivelore index memories",
|
|
9457
9587
|
section: "Index health"
|
|
9458
9588
|
});
|
|
9459
9589
|
}
|
|
@@ -10889,7 +11019,7 @@ async function checkBootstrapComplete(paths, config, productionCodeChanged) {
|
|
|
10889
11019
|
}
|
|
10890
11020
|
const memories = existsSync42(paths.memoriesDir) ? await loadMemoriesFromDir15(paths.memoriesDir) : [];
|
|
10891
11021
|
const codeMap = await loadCodeMap7(paths);
|
|
10892
|
-
const codeFiles = codeMap ? Object.keys(codeMap.files) : [];
|
|
11022
|
+
const codeFiles = codeMap ? Object.keys(codeMap.files).filter((file) => existsSync42(path40.join(paths.root, file))) : [];
|
|
10893
11023
|
let existingModules = [];
|
|
10894
11024
|
try {
|
|
10895
11025
|
const entries = await readdir4(paths.modulesContextDir, { withFileTypes: true });
|
|
@@ -10953,7 +11083,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
10953
11083
|
findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
|
|
10954
11084
|
});
|
|
10955
11085
|
}
|
|
10956
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
11086
|
+
findings.push(...await inspectIntegrationVersions(root, "0.44.0"));
|
|
10957
11087
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
10958
11088
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
10959
11089
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
|
|
@@ -10987,7 +11117,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
10987
11117
|
findings.push(...await verifyDecisionCoverage(paths, stage, sessionId));
|
|
10988
11118
|
}
|
|
10989
11119
|
if (stage === "pre-commit" || stage === "ci") {
|
|
10990
|
-
findings.push(...await runPrecommitPolicy(paths, config.enforcement?.antiPatternGate ?? "anchored", stage));
|
|
11120
|
+
findings.push(...await runPrecommitPolicy(paths, config.enforcement?.antiPatternGate ?? "anchored", stage, config));
|
|
10991
11121
|
} else if (stage === "local") {
|
|
10992
11122
|
findings.push({
|
|
10993
11123
|
severity: "info",
|
|
@@ -11194,17 +11324,28 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
11194
11324
|
impact: Math.min(35, 10 + missing.length * 5)
|
|
11195
11325
|
}];
|
|
11196
11326
|
}
|
|
11197
|
-
async function runPrecommitPolicy(paths, gate, stage) {
|
|
11327
|
+
async function runPrecommitPolicy(paths, gate, stage, config) {
|
|
11198
11328
|
const snapshot = await getPolicyDiffSnapshot(paths.root, stage);
|
|
11199
|
-
const
|
|
11329
|
+
const weakeningApprovals = await resolveSensorWeakeningApprovals(paths.root, stage);
|
|
11330
|
+
const detectedWeakenings = detectSensorWeakening(snapshot.diff);
|
|
11331
|
+
const weakenings = detectedWeakenings.filter((weakening) => !weakeningApprovals.has(weakening.memory_id));
|
|
11332
|
+
const approvedWeakenings = detectedWeakenings.filter((weakening) => weakeningApprovals.has(weakening.memory_id));
|
|
11200
11333
|
const weakeningFindings = weakenings.length > 0 ? [{
|
|
11201
|
-
severity: "warn",
|
|
11334
|
+
severity: config.enforcement?.sensorWeakeningGate === "block" ? "error" : "warn",
|
|
11202
11335
|
code: "sensor-weakened",
|
|
11203
11336
|
message: `This diff weakens the enforcement surface \u2014 ${weakenings.length} sensor change(s) need review: ` + weakenings.slice(0, 5).map((w) => `${w.memory_id} (${w.change}: ${w.detail})`).join(", ") + (weakenings.length > 5 ? ", \u2026" : "") + ".",
|
|
11204
|
-
fix: "If
|
|
11337
|
+
fix: "If intentional, add `Hivelore-Sensor-Change: <memory-id>` to the commit message and set `HIVELORE_SENSOR_WEAKENING_APPROVALS=<memory-id>` for the local commit hook; otherwise restore the sensor.",
|
|
11205
11338
|
memory_ids: [...new Set(weakenings.map((w) => w.memory_id))].slice(0, 10),
|
|
11206
|
-
impact: 8
|
|
11339
|
+
impact: config.enforcement?.sensorWeakeningGate === "block" ? 30 : 8
|
|
11207
11340
|
}] : [];
|
|
11341
|
+
if (approvedWeakenings.length > 0) {
|
|
11342
|
+
weakeningFindings.push({
|
|
11343
|
+
severity: "ok",
|
|
11344
|
+
code: "sensor-weakening-approved",
|
|
11345
|
+
message: `Reviewed sensor change(s) approved for ${[...new Set(approvedWeakenings.map((w) => w.memory_id))].join(", ")}.`,
|
|
11346
|
+
memory_ids: [...new Set(approvedWeakenings.map((w) => w.memory_id))]
|
|
11347
|
+
});
|
|
11348
|
+
}
|
|
11208
11349
|
if (gate === "off") {
|
|
11209
11350
|
return [
|
|
11210
11351
|
{ severity: "info", code: "precommit-policy-off", message: "Anti-pattern gate is disabled (enforcement.antiPatternGate=off)." },
|
|
@@ -11290,6 +11431,28 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
11290
11431
|
...weakeningFindings
|
|
11291
11432
|
];
|
|
11292
11433
|
}
|
|
11434
|
+
function parseSensorWeakeningApprovals(text) {
|
|
11435
|
+
const approved = /* @__PURE__ */ new Set();
|
|
11436
|
+
if (!text) return approved;
|
|
11437
|
+
const trailer = /^Hivelore-Sensor-Change:\s*(.+)$/gmi;
|
|
11438
|
+
for (const match of text.matchAll(trailer)) {
|
|
11439
|
+
for (const id of (match[1] ?? "").split(/[\s,]+/)) if (id) approved.add(id);
|
|
11440
|
+
}
|
|
11441
|
+
if (!text.includes("Hivelore-Sensor-Change:")) {
|
|
11442
|
+
for (const id of text.split(/[\s,]+/)) if (id) approved.add(id);
|
|
11443
|
+
}
|
|
11444
|
+
return approved;
|
|
11445
|
+
}
|
|
11446
|
+
async function resolveSensorWeakeningApprovals(root, stage) {
|
|
11447
|
+
const approved = parseSensorWeakeningApprovals(process.env["HIVELORE_SENSOR_WEAKENING_APPROVALS"]);
|
|
11448
|
+
if (stage !== "ci") return approved;
|
|
11449
|
+
try {
|
|
11450
|
+
const { stdout } = await execFileAsync2("git", ["log", "-1", "--pretty=%B"], { cwd: root });
|
|
11451
|
+
for (const id of parseSensorWeakeningApprovals(stdout)) approved.add(id);
|
|
11452
|
+
} catch {
|
|
11453
|
+
}
|
|
11454
|
+
return approved;
|
|
11455
|
+
}
|
|
11293
11456
|
async function stagedFileContent(root, rel) {
|
|
11294
11457
|
try {
|
|
11295
11458
|
return await runCommand2("git", ["show", `:${rel}`], root);
|
|
@@ -11367,7 +11530,7 @@ async function runSensorGate(paths, diff, stage) {
|
|
|
11367
11530
|
} else {
|
|
11368
11531
|
for (const memory2 of astSensorMemories) {
|
|
11369
11532
|
const sensor = memory2.frontmatter.sensor;
|
|
11370
|
-
if (!sensor.pattern) continue;
|
|
11533
|
+
if (!sensor.pattern && !sensor.rule) continue;
|
|
11371
11534
|
const applicable = targets.filter((t) => sensorAppliesToPath2(sensor, memory2.frontmatter.anchor.paths, t.path));
|
|
11372
11535
|
if (applicable.length === 0) continue;
|
|
11373
11536
|
let fired = false;
|
|
@@ -11378,6 +11541,8 @@ async function runSensorGate(paths, diff, stage) {
|
|
|
11378
11541
|
if (content === null) continue;
|
|
11379
11542
|
const scan = await runAstSensorOnContent({
|
|
11380
11543
|
pattern: sensor.pattern,
|
|
11544
|
+
rule: sensor.rule,
|
|
11545
|
+
language: sensor.language,
|
|
11381
11546
|
absent: sensor.absent,
|
|
11382
11547
|
content,
|
|
11383
11548
|
filePath: target.path,
|
|
@@ -11422,7 +11587,7 @@ async function runSensorGate(paths, diff, stage) {
|
|
|
11422
11587
|
}
|
|
11423
11588
|
}
|
|
11424
11589
|
}
|
|
11425
|
-
const config = await loadConfig12(paths).catch(() =>
|
|
11590
|
+
const config = await loadConfig12(paths).catch(() => ({}));
|
|
11426
11591
|
if (config?.enforcement?.runCommandSensors === true) {
|
|
11427
11592
|
const changedPaths = targets.map((t) => t.path).filter(Boolean);
|
|
11428
11593
|
const specs = selectCommandSensors(scannable, changedPaths).filter((sp) => !seen.has(sp.memory_id));
|
|
@@ -11460,13 +11625,14 @@ async function runSensorGate(paths, diff, stage) {
|
|
|
11460
11625
|
if (run.status === "passed") continue;
|
|
11461
11626
|
seen.add(run.memory_id);
|
|
11462
11627
|
if (run.status === "unrunnable") {
|
|
11628
|
+
const strictUnrunnable = config.enforcement?.commandSensorUnrunnable === "block";
|
|
11463
11629
|
findings.push({
|
|
11464
|
-
severity: "warn",
|
|
11630
|
+
severity: strictUnrunnable ? "error" : "warn",
|
|
11465
11631
|
code: "command-sensor-unrunnable",
|
|
11466
11632
|
message: `Command sensor ${run.memory_id} could not run (${run.unrunnable_reason}): \`${run.command}\`` + (run.output_tail ? `
|
|
11467
11633
|
${run.output_tail}` : ""),
|
|
11468
11634
|
fix: "Fix the sensor's command (or its timeout_ms), or demote it: `hivelore sensors promote <id> --severity warn`.",
|
|
11469
|
-
impact: 5
|
|
11635
|
+
impact: strictUnrunnable ? 35 : 5
|
|
11470
11636
|
});
|
|
11471
11637
|
continue;
|
|
11472
11638
|
}
|
|
@@ -12564,6 +12730,14 @@ import {
|
|
|
12564
12730
|
withoutQuarantineNote
|
|
12565
12731
|
} from "@hivelore/core";
|
|
12566
12732
|
var exec5 = promisify7(execFile7);
|
|
12733
|
+
function rejectProposal(opts, reason, guidance) {
|
|
12734
|
+
if (opts.json) console.log(JSON.stringify({ accepted: false, reason, guidance }, null, 2));
|
|
12735
|
+
else {
|
|
12736
|
+
ui.error(`Rejected (${reason}).`);
|
|
12737
|
+
ui.warn(guidance);
|
|
12738
|
+
}
|
|
12739
|
+
process.exitCode = 1;
|
|
12740
|
+
}
|
|
12567
12741
|
function registerSensors(program2) {
|
|
12568
12742
|
const sensors = program2.command("sensors").description("Operate executable sensors derived from Hivelore memories");
|
|
12569
12743
|
sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
@@ -12589,6 +12763,7 @@ function registerSensors(program2) {
|
|
|
12589
12763
|
);
|
|
12590
12764
|
if ("absent" in row && row.absent) console.log(` ${ui.dim("only when missing:")} ${row.absent}`);
|
|
12591
12765
|
if (row.paths.length > 0) console.log(` ${ui.dim("paths:")} ${row.paths.join(", ")}`);
|
|
12766
|
+
if (row.red_proven) console.log(` ${ui.green("\u2713 RED-proven")}${row.incident ? ` \u2014 ${row.incident}` : ""}`);
|
|
12592
12767
|
if (row.last_fired) console.log(` ${ui.dim("last fired:")} ${row.last_fired}`);
|
|
12593
12768
|
if (brittle) console.log(` ${ui.yellow("\u26A0 brittle:")} ${brittle} \u2014 consider rewriting or retiring this sensor`);
|
|
12594
12769
|
}
|
|
@@ -12603,7 +12778,10 @@ function registerSensors(program2) {
|
|
|
12603
12778
|
const targets = scannableSensorTargets(diff);
|
|
12604
12779
|
const hits = runSensors2(memories, targets);
|
|
12605
12780
|
const astMemories = (await runnableSensorMemories(paths, false)).filter(
|
|
12606
|
-
(m) =>
|
|
12781
|
+
(m) => {
|
|
12782
|
+
const sensor = m.frontmatter.sensor;
|
|
12783
|
+
return sensor?.kind === "ast" && Boolean(sensor.pattern || sensor.rule);
|
|
12784
|
+
}
|
|
12607
12785
|
);
|
|
12608
12786
|
const astHits = [];
|
|
12609
12787
|
let astUnrunnable = 0;
|
|
@@ -12631,6 +12809,8 @@ function registerSensors(program2) {
|
|
|
12631
12809
|
if (content === null) continue;
|
|
12632
12810
|
const scan = await runAstSensorOnContent({
|
|
12633
12811
|
pattern: sensor.pattern,
|
|
12812
|
+
rule: sensor.rule,
|
|
12813
|
+
language: sensor.language,
|
|
12634
12814
|
absent: sensor.absent,
|
|
12635
12815
|
content,
|
|
12636
12816
|
filePath: target.path,
|
|
@@ -12798,6 +12978,11 @@ function registerSensors(program2) {
|
|
|
12798
12978
|
process.exitCode = 1;
|
|
12799
12979
|
return;
|
|
12800
12980
|
}
|
|
12981
|
+
if (severity === "block" && (sensor.kind === "shell" || sensor.kind === "test") && sensor.red_proven !== true) {
|
|
12982
|
+
ui.error("Refusing to promote an unproven command/test oracle to block. Re-propose it with --red-ref <pre-fix-commit>.");
|
|
12983
|
+
process.exitCode = 1;
|
|
12984
|
+
return;
|
|
12985
|
+
}
|
|
12801
12986
|
const brittle = sensor.kind === "regex" && sensor.pattern ? sensorPatternBrittleness2(sensor.pattern) : null;
|
|
12802
12987
|
if (severity === "block" && brittle && !opts.force) {
|
|
12803
12988
|
ui.error(`Refusing to block on a brittle sensor (${brittle}). Rewrite the pattern, or pass --force.`);
|
|
@@ -12844,25 +13029,36 @@ function registerSensors(program2) {
|
|
|
12844
13029
|
});
|
|
12845
13030
|
sensors.command("propose").description(
|
|
12846
13031
|
"Propose a discriminating sensor for a memory \u2014 you write the pattern, Hivelore validates it before\n trusting it to block. Mirrors the MCP `propose_sensor` tool (the agent-authored path).\n\n A `block` proposal is accepted ONLY if it is not brittle, stays SILENT on the current code,\n and FIRES on the bad example. Rejected proposals are not written \u2014 fix and re-run.\n\n Example:\n hivelore sensors propose <memory-id> \\\n --pattern 'stripe\\.paymentIntents\\.create' --absent 'idempotencyKey' \\\n --bad-example 'stripe.paymentIntents.create({ amount })'"
|
|
12847
|
-
).argument("<memory-id>", "memory id to attach the sensor to").option("--kind <kind>", "regex (default) | ast (structural \u2014 comments/strings can't false-positive) | shell | test (route the team's own oracle)", "regex").option("--pattern <regex>", "kind=regex: regex matching the FAULTY usage").option("--command <cmd>", "kind=shell|test: command the gate runs when the diff touches the sensor's paths").option("--timeout <ms>", "kind=shell|test: max runtime in ms (default 120000)").option("--absent <regex>", "regex for the CORRECT-usage marker (makes it discriminate)").option("--bad-example <code>", "a snippet that SHOULD match (else examples are read from the lesson)").option("--severity <severity>", "block | warn", "block").option("--message <text>", "fix message shown when it fires").option("--incident <ref>", "provenance: the incident this sensor guards (e.g. 'prod #442') \u2014 shown when it fires and in the receipt").option("--red-ref <ref>", "kind=shell|test: pre-fix commit/ref \u2014 validation replays it in a scratch worktree and requires the oracle to FAIL there (records red_proven)").option("--flags <flags>", "regex flags (e.g. i)").option("--paths <csv>", "override scope paths (defaults to the memory anchors)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
13032
|
+
).argument("<memory-id>", "memory id to attach the sensor to").option("--kind <kind>", "regex (default) | ast (structural \u2014 comments/strings can't false-positive) | shell | test (route the team's own oracle)", "regex").option("--pattern <regex>", "kind=regex: regex matching the FAULTY usage").option("--rule <json>", "kind=ast: full ast-grep Rule JSON (kind/inside/has/not/all/any)").option("--language <name>", "kind=ast: explicit language for non-standard extensions").option("--command <cmd>", "kind=shell|test: command the gate runs when the diff touches the sensor's paths").option("--timeout <ms>", "kind=shell|test: max runtime in ms (default 120000)").option("--absent <regex>", "regex for the CORRECT-usage marker (makes it discriminate)").option("--bad-example <code>", "a snippet that SHOULD match (else examples are read from the lesson)").option("--severity <severity>", "block | warn", "block").option("--message <text>", "fix message shown when it fires").option("--incident <ref>", "provenance: the incident this sensor guards (e.g. 'prod #442') \u2014 shown when it fires and in the receipt").option("--red-ref <ref>", "kind=shell|test: pre-fix commit/ref \u2014 validation replays it in a scratch worktree and requires the oracle to FAIL there (records red_proven)").option("--flags <flags>", "regex flags (e.g. i)").option("--paths <csv>", "override scope paths (defaults to the memory anchors)").option("--json", "emit a machine-readable proposal verdict", false).option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
12848
13033
|
if (opts.kind === "shell" || opts.kind === "test" || opts.kind === "ast") {
|
|
12849
13034
|
if ((opts.kind === "shell" || opts.kind === "test") && !opts.command?.trim()) {
|
|
12850
|
-
|
|
12851
|
-
process.exitCode = 1;
|
|
13035
|
+
rejectProposal(opts, "missing-command", "--kind shell|test requires --command.");
|
|
12852
13036
|
return;
|
|
12853
13037
|
}
|
|
12854
|
-
if (opts.kind === "ast" && !opts.pattern?.trim()) {
|
|
12855
|
-
|
|
12856
|
-
process.exitCode = 1;
|
|
13038
|
+
if (opts.kind === "ast" && !opts.pattern?.trim() && !opts.rule?.trim()) {
|
|
13039
|
+
rejectProposal(opts, "missing-ast-rule", "--kind ast requires --pattern or --rule <json>.");
|
|
12857
13040
|
return;
|
|
12858
13041
|
}
|
|
13042
|
+
let astRule;
|
|
13043
|
+
if (opts.rule?.trim()) {
|
|
13044
|
+
try {
|
|
13045
|
+
const parsed = JSON.parse(opts.rule);
|
|
13046
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("rule must be a JSON object");
|
|
13047
|
+
astRule = parsed;
|
|
13048
|
+
} catch (err) {
|
|
13049
|
+
rejectProposal(opts, "invalid-ast-rule", `--rule must be valid ast-grep Rule JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
13050
|
+
return;
|
|
13051
|
+
}
|
|
13052
|
+
}
|
|
12859
13053
|
const root2 = findProjectRoot39(opts.dir);
|
|
12860
|
-
const { proposeSensor } = await import("./server-
|
|
13054
|
+
const { proposeSensor } = await import("./server-MCNCAFJN.js");
|
|
12861
13055
|
const out = await proposeSensor(
|
|
12862
13056
|
{
|
|
12863
13057
|
memory_id: id,
|
|
12864
13058
|
kind: opts.kind,
|
|
12865
|
-
pattern: opts.kind === "ast" ? opts.pattern
|
|
13059
|
+
pattern: opts.kind === "ast" ? opts.pattern?.trim() : void 0,
|
|
13060
|
+
rule: astRule,
|
|
13061
|
+
language: opts.kind === "ast" ? opts.language : void 0,
|
|
12866
13062
|
command: opts.command?.trim(),
|
|
12867
13063
|
timeout_ms: opts.timeout ? Math.max(1, Number(opts.timeout)) : void 0,
|
|
12868
13064
|
absent: opts.kind === "ast" ? opts.absent : void 0,
|
|
@@ -12876,6 +13072,11 @@ function registerSensors(program2) {
|
|
|
12876
13072
|
},
|
|
12877
13073
|
{ paths: resolveHaivePaths36(root2) }
|
|
12878
13074
|
);
|
|
13075
|
+
if (opts.json) {
|
|
13076
|
+
console.log(JSON.stringify(out, null, 2));
|
|
13077
|
+
if (!out.accepted) process.exitCode = 1;
|
|
13078
|
+
return;
|
|
13079
|
+
}
|
|
12879
13080
|
if (out.accepted) {
|
|
12880
13081
|
ui.success(`${opts.kind === "ast" ? "AST" : "Command"} sensor accepted (${out.severity}) on ${id}`);
|
|
12881
13082
|
ui.info(` ${out.guidance}`);
|
|
@@ -12887,8 +13088,7 @@ function registerSensors(program2) {
|
|
|
12887
13088
|
return;
|
|
12888
13089
|
}
|
|
12889
13090
|
if (!opts.pattern?.trim()) {
|
|
12890
|
-
|
|
12891
|
-
process.exitCode = 1;
|
|
13091
|
+
rejectProposal(opts, "missing-pattern", "kind=regex requires --pattern.");
|
|
12892
13092
|
return;
|
|
12893
13093
|
}
|
|
12894
13094
|
const severity = opts.severity === "warn" ? "warn" : "block";
|
|
@@ -12896,8 +13096,7 @@ function registerSensors(program2) {
|
|
|
12896
13096
|
new RegExp(opts.pattern, opts.flags ?? "");
|
|
12897
13097
|
if (opts.absent) new RegExp(opts.absent, opts.flags ?? "");
|
|
12898
13098
|
} catch (err) {
|
|
12899
|
-
|
|
12900
|
-
process.exitCode = 1;
|
|
13099
|
+
rejectProposal(opts, "invalid-regex", `Invalid regex: ${String(err)}`);
|
|
12901
13100
|
return;
|
|
12902
13101
|
}
|
|
12903
13102
|
const root = findProjectRoot39(opts.dir);
|
|
@@ -12905,8 +13104,7 @@ function registerSensors(program2) {
|
|
|
12905
13104
|
const loaded = existsSync44(paths.memoriesDir) ? await loadMemoriesFromDir16(paths.memoriesDir) : [];
|
|
12906
13105
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
12907
13106
|
if (!found) {
|
|
12908
|
-
|
|
12909
|
-
process.exitCode = 1;
|
|
13107
|
+
rejectProposal(opts, "memory-not-found", `No memory found with id ${id}`);
|
|
12910
13108
|
return;
|
|
12911
13109
|
}
|
|
12912
13110
|
const anchorPaths = opts.paths ? opts.paths.split(",").map((s) => s.trim()).filter(Boolean) : found.memory.frontmatter.anchor.paths;
|
|
@@ -12928,6 +13126,18 @@ function registerSensors(program2) {
|
|
|
12928
13126
|
last_fired: null
|
|
12929
13127
|
};
|
|
12930
13128
|
const verdict = judgeProposedSensor(sensor, { currentTargets, badExamples });
|
|
13129
|
+
if (opts.json && !verdict.accepted) {
|
|
13130
|
+
console.log(JSON.stringify({
|
|
13131
|
+
accepted: verdict.accepted,
|
|
13132
|
+
reason: verdict.reason ?? null,
|
|
13133
|
+
severity,
|
|
13134
|
+
memory_id: id,
|
|
13135
|
+
self_check: verdict.self_check,
|
|
13136
|
+
brittle: verdict.brittle
|
|
13137
|
+
}, null, 2));
|
|
13138
|
+
if (!verdict.accepted) process.exitCode = 1;
|
|
13139
|
+
return;
|
|
13140
|
+
}
|
|
12931
13141
|
if (!verdict.accepted) {
|
|
12932
13142
|
ui.error(`Rejected (${verdict.reason}).`);
|
|
12933
13143
|
if (verdict.reason === "fires-on-current") {
|
|
@@ -12941,6 +13151,17 @@ function registerSensors(program2) {
|
|
|
12941
13151
|
return;
|
|
12942
13152
|
}
|
|
12943
13153
|
await writeFile27(found.filePath, serializeMemory15({ frontmatter: { ...found.memory.frontmatter, sensor }, body: found.memory.body }), "utf8");
|
|
13154
|
+
if (opts.json) {
|
|
13155
|
+
console.log(JSON.stringify({
|
|
13156
|
+
accepted: true,
|
|
13157
|
+
reason: null,
|
|
13158
|
+
severity,
|
|
13159
|
+
memory_id: id,
|
|
13160
|
+
self_check: verdict.self_check,
|
|
13161
|
+
brittle: verdict.brittle
|
|
13162
|
+
}, null, 2));
|
|
13163
|
+
return;
|
|
13164
|
+
}
|
|
12944
13165
|
ui.success(`Sensor accepted (${severity}) on ${id}`);
|
|
12945
13166
|
ui.info(`pattern=${JSON.stringify(opts.pattern)}${opts.absent ? ` absent=${JSON.stringify(opts.absent)}` : ""}`);
|
|
12946
13167
|
ui.info(
|
|
@@ -13057,10 +13278,14 @@ async function sensorRows(paths) {
|
|
|
13057
13278
|
kind: sensor.kind,
|
|
13058
13279
|
severity: sensor.severity,
|
|
13059
13280
|
pattern: sensor.pattern,
|
|
13281
|
+
rule: sensor.rule,
|
|
13282
|
+
language: sensor.language,
|
|
13060
13283
|
absent: sensor.absent,
|
|
13061
13284
|
command: sensor.command,
|
|
13062
13285
|
paths: sensor.paths.length > 0 ? sensor.paths : memory2.frontmatter.anchor.paths,
|
|
13063
13286
|
message: sensor.message,
|
|
13287
|
+
incident: sensor.incident,
|
|
13288
|
+
red_proven: sensor.red_proven === true,
|
|
13064
13289
|
autogen: sensor.autogen,
|
|
13065
13290
|
last_fired: sensor.last_fired,
|
|
13066
13291
|
...brittle ? { brittle } : {}
|
|
@@ -13115,6 +13340,7 @@ import { mkdir as mkdir18, readFile as readFile21, writeFile as writeFile28 } fr
|
|
|
13115
13340
|
import path43 from "path";
|
|
13116
13341
|
import "commander";
|
|
13117
13342
|
import {
|
|
13343
|
+
appendProposedRetrievalCases as appendProposedRetrievalCases2,
|
|
13118
13344
|
draftsFromFindings,
|
|
13119
13345
|
filterNewDrafts,
|
|
13120
13346
|
findProjectRoot as findProjectRoot40,
|
|
@@ -13248,6 +13474,7 @@ function registerIngest(program2) {
|
|
|
13248
13474
|
const created2 = [];
|
|
13249
13475
|
if (!opts.dryRun) {
|
|
13250
13476
|
for (const draft of fresh) created2.push(await writeDraft(paths, draft));
|
|
13477
|
+
if (format === "github-pr") await appendReviewGoldenCases(root, fresh);
|
|
13251
13478
|
}
|
|
13252
13479
|
console.log(
|
|
13253
13480
|
JSON.stringify(
|
|
@@ -13300,6 +13527,7 @@ function registerIngest(program2) {
|
|
|
13300
13527
|
await writeDraft(paths, draft);
|
|
13301
13528
|
created++;
|
|
13302
13529
|
}
|
|
13530
|
+
if (format === "github-pr") await appendReviewGoldenCases(root, fresh);
|
|
13303
13531
|
ui.success(`Created ${created} proposed memory(ies) under ${path43.relative(root, paths.memoriesDir)}/`);
|
|
13304
13532
|
ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
|
|
13305
13533
|
});
|
|
@@ -13310,6 +13538,19 @@ async function writeDraft(paths, draft) {
|
|
|
13310
13538
|
await writeFile28(file, serializeMemory16({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
13311
13539
|
return file;
|
|
13312
13540
|
}
|
|
13541
|
+
async function appendReviewGoldenCases(root, drafts) {
|
|
13542
|
+
if (drafts.length === 0) return;
|
|
13543
|
+
const specFile = path43.join(root, ".ai", "eval", "spec.json");
|
|
13544
|
+
const current = existsSync45(specFile) ? await readFile21(specFile, "utf8") : null;
|
|
13545
|
+
const cases = drafts.map((draft) => ({
|
|
13546
|
+
name: `review-learning:${draft.topic}`,
|
|
13547
|
+
task: draft.finding.message,
|
|
13548
|
+
...draft.finding.path ? { files: [draft.finding.path] } : {},
|
|
13549
|
+
expect_ids: [draft.frontmatter.id]
|
|
13550
|
+
}));
|
|
13551
|
+
await mkdir18(path43.dirname(specFile), { recursive: true });
|
|
13552
|
+
await writeFile28(specFile, appendProposedRetrievalCases2(current, cases), "utf8");
|
|
13553
|
+
}
|
|
13313
13554
|
async function fetchSonarIssues(opts) {
|
|
13314
13555
|
const baseUrl = (opts.sonarUrl ?? process.env.SONAR_HOST_URL ?? "").trim().replace(/\/+$/, "");
|
|
13315
13556
|
const token = (opts.sonarToken ?? process.env.SONAR_TOKEN ?? "").trim();
|
|
@@ -13356,17 +13597,29 @@ async function fetchPrReviewComments(root, ref) {
|
|
|
13356
13597
|
const { promisify: promisify9 } = await import("util");
|
|
13357
13598
|
const run = promisify9(execFile9);
|
|
13358
13599
|
try {
|
|
13359
|
-
const
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13600
|
+
const fetchPages = async (endpoint) => {
|
|
13601
|
+
const { stdout } = await run(
|
|
13602
|
+
"gh",
|
|
13603
|
+
["api", endpoint, "--paginate", "--slurp"],
|
|
13604
|
+
{ cwd: root, maxBuffer: 16 * 1024 * 1024 }
|
|
13605
|
+
);
|
|
13606
|
+
const pages = JSON.parse(stdout);
|
|
13607
|
+
return Array.isArray(pages) ? pages.flatMap((page) => Array.isArray(page) ? page : []) : [];
|
|
13608
|
+
};
|
|
13609
|
+
const [reviewComments, issueComments] = await Promise.all([
|
|
13610
|
+
fetchPages(`repos/{owner}/{repo}/pulls/${prNumber}/comments`),
|
|
13611
|
+
fetchPages(`repos/{owner}/{repo}/issues/${prNumber}/comments`)
|
|
13612
|
+
]);
|
|
13613
|
+
const normalizedIssueComments = issueComments.map((row) => ({
|
|
13614
|
+
...row,
|
|
13615
|
+
pull_request_url: `https://api.github.com/repos/{owner}/{repo}/pulls/${prNumber}`
|
|
13616
|
+
}));
|
|
13617
|
+
return { ok: true, json: JSON.stringify([...reviewComments, ...normalizedIssueComments]) };
|
|
13365
13618
|
} catch (err) {
|
|
13366
13619
|
const e = err;
|
|
13367
13620
|
return {
|
|
13368
13621
|
ok: false,
|
|
13369
|
-
error: `Could not fetch PR #${prNumber}
|
|
13622
|
+
error: `Could not fetch PR #${prNumber} comments via gh: ${(e.stderr || e.message || String(err)).slice(0, 200)}. Install/authenticate the gh CLI, or pass a recorded comments JSON file instead.`
|
|
13370
13623
|
};
|
|
13371
13624
|
}
|
|
13372
13625
|
}
|
|
@@ -13829,7 +14082,7 @@ function registerBridges(program2) {
|
|
|
13829
14082
|
|
|
13830
14083
|
// src/index.ts
|
|
13831
14084
|
var program = new Command48();
|
|
13832
|
-
program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.
|
|
14085
|
+
program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.44.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
|
|
13833
14086
|
registerInit(program);
|
|
13834
14087
|
registerResolveProject(program);
|
|
13835
14088
|
registerEnforce(program);
|