@hivelore/cli 0.35.1 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  preCommitCheck,
11
11
  readPresumedCorrectTargets,
12
12
  runHaiveMcpStdio
13
- } from "./chunk-QQANGHJK.js";
13
+ } from "./chunk-EWJQ3YE7.js";
14
14
  import {
15
15
  registerMemoryPending
16
16
  } from "./chunk-OYJKHD22.js";
@@ -3755,7 +3755,7 @@ ${SEED_FOOTER(stack)}` });
3755
3755
 
3756
3756
  // src/commands/init.ts
3757
3757
  var execFileAsync = promisify2(execFile2);
3758
- var HAIVE_GITHUB_ACTION_REF = `v${"0.35.1"}`;
3758
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.36.0"}`;
3759
3759
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3760
3760
 
3761
3761
  > Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
@@ -7325,13 +7325,15 @@ import {
7325
7325
  loadPreventionEvents as loadPreventionEvents2,
7326
7326
  parseSince,
7327
7327
  readUsageEvents,
7328
+ renderPreventionReceiptShare,
7328
7329
  resolveHaivePaths as resolveHaivePaths27,
7329
7330
  usageLogSize
7330
7331
  } from "@hivelore/core";
7331
7332
  function registerStats(program2) {
7332
7333
  const stats = program2.command("stats").description("Show MCP tool-usage stats and prevention receipts.");
7333
- stats.command("receipt").description("Show documented mistakes refused by the gate over a time window").addHelpText("after", "\nParent options also apply: --since <window> (default 7d here), --json, --dir <dir>.").action(async () => {
7334
+ const receiptCmd = stats.command("receipt").description("Show documented mistakes refused by the gate over a time window").option("--share", "emit a Markdown block ready to paste into Slack or a PR (with attribution)", false).addHelpText("after", "\nParent options also apply: --since <window> (default 7d here), --json, --dir <dir>.").action(async () => {
7334
7335
  const opts = stats.opts();
7336
+ const sub = receiptCmd.opts();
7335
7337
  const root = findProjectRoot28(opts.dir);
7336
7338
  const paths = resolveHaivePaths27(root);
7337
7339
  const sinceRaw = stats.getOptionValueSource("since") === "default" ? "7d" : opts.since ?? "7d";
@@ -7342,7 +7344,8 @@ function registerStats(program2) {
7342
7344
  existsSync33(paths.memoriesDir) ? loadMemoriesFromDir11(paths.memoriesDir) : Promise.resolve([])
7343
7345
  ]);
7344
7346
  const receipt = buildPreventionReceipt(events, memories, usage, { since });
7345
- console.log(opts.json ? JSON.stringify(receipt, null, 2) : renderPreventionReceipt(receipt));
7347
+ const output = sub.share ? renderPreventionReceiptShare(receipt) : opts.json ? JSON.stringify(receipt, null, 2) : renderPreventionReceipt(receipt);
7348
+ console.log(output);
7346
7349
  });
7347
7350
  stats.option("--since <window>", "ISO date or relative (e.g. '7d', '24h', '30m')", "30d").option("--json", "emit JSON instead of human-readable output", false).option("--memory-hits", "show top-read memories (which mems are actually being used)", false).option(
7348
7351
  "--export-report <path>",
@@ -8956,7 +8959,7 @@ function registerDoctor(program2) {
8956
8959
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
8957
8960
  });
8958
8961
  }
8959
- findings.push(...await collectInstallFindings(root, "0.35.1"));
8962
+ findings.push(...await collectInstallFindings(root, "0.36.0"));
8960
8963
  findings.push(...await collectToolchainFindings(root));
8961
8964
  try {
8962
8965
  const legacyRaw = execSync("haive-mcp --version", {
@@ -8964,7 +8967,7 @@ function registerDoctor(program2) {
8964
8967
  timeout: 3e3,
8965
8968
  stdio: ["ignore", "pipe", "ignore"]
8966
8969
  }).trim();
8967
- const cliVersion = "0.35.1";
8970
+ const cliVersion = "0.36.0";
8968
8971
  if (legacyRaw && legacyRaw !== cliVersion) {
8969
8972
  findings.push({
8970
8973
  severity: "warn",
@@ -9723,6 +9726,7 @@ import {
9723
9726
  readRecentBriefingMarker,
9724
9727
  recordPreventionHits,
9725
9728
  resolveBriefingBudget as resolveBriefingBudget2,
9729
+ incidentSuffix,
9726
9730
  resolveHaivePaths as resolveHaivePaths35,
9727
9731
  runSensors,
9728
9732
  saveConfig as saveConfig3,
@@ -9872,7 +9876,8 @@ async function executeCommandSensor(spec, root) {
9872
9876
  command: spec.command,
9873
9877
  kind: spec.kind,
9874
9878
  severity: spec.severity,
9875
- message: spec.message
9879
+ message: spec.message,
9880
+ ...spec.incident ? { incident: spec.incident } : {}
9876
9881
  };
9877
9882
  try {
9878
9883
  const { stdout, stderr } = await exec2("bash", ["-c", spec.command], {
@@ -10665,7 +10670,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
10665
10670
  findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
10666
10671
  });
10667
10672
  }
10668
- findings.push(...await inspectIntegrationVersions(root, "0.35.1"));
10673
+ findings.push(...await inspectIntegrationVersions(root, "0.36.0"));
10669
10674
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
10670
10675
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
10671
10676
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
@@ -11020,7 +11025,7 @@ async function runSensorGate(paths, diff, stage) {
11020
11025
  findings.push({
11021
11026
  severity: "error",
11022
11027
  code: "sensor-block",
11023
- message: `Block sensor fired \u2014 ${hit.memory_id}: ${hit.message}${where}`,
11028
+ message: `Block sensor fired \u2014 ${hit.memory_id}: ${hit.message}${where}${incidentSuffix(hit.sensor.incident)}`,
11024
11029
  fix: "Remove the flagged pattern, or run `hivelore sensors check` to inspect the match.",
11025
11030
  impact: 45,
11026
11031
  memory_ids: [hit.memory_id]
@@ -11029,7 +11034,7 @@ async function runSensorGate(paths, diff, stage) {
11029
11034
  findings.push({
11030
11035
  severity: "warn",
11031
11036
  code: "sensor-warn",
11032
- message: `Sensor flagged ${hit.memory_id}: ${hit.message}${where}`,
11037
+ message: `Sensor flagged ${hit.memory_id}: ${hit.message}${where}${incidentSuffix(hit.sensor.incident)}`,
11033
11038
  fix: "Review the flagged line; `hivelore sensors check` shows the matched code.",
11034
11039
  impact: 5,
11035
11040
  memory_ids: [hit.memory_id]
@@ -11091,7 +11096,7 @@ ${run.output_tail}` : "";
11091
11096
  findings.push({
11092
11097
  severity: "error",
11093
11098
  code: "sensor-block",
11094
- message: `Block ${run.kind} sensor fired \u2014 ${run.memory_id}: ${run.message}
11099
+ message: `Block ${run.kind} sensor fired \u2014 ${run.memory_id}: ${run.message}${incidentSuffix(run.incident)}
11095
11100
  command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlock}`,
11096
11101
  fix: "Fix the behaviour the command checks, or run `hivelore sensors check --commands` to inspect it.",
11097
11102
  impact: 45,
@@ -11101,7 +11106,7 @@ command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlo
11101
11106
  findings.push({
11102
11107
  severity: "warn",
11103
11108
  code: "sensor-warn",
11104
- message: `${run.kind} sensor flagged ${run.memory_id}: ${run.message} (exit ${run.exit_code})${outputBlock}`,
11109
+ message: `${run.kind} sensor flagged ${run.memory_id}: ${run.message}${incidentSuffix(run.incident)} (exit ${run.exit_code})${outputBlock}`,
11105
11110
  fix: "Review the failing command; `hivelore sensors check --commands` re-runs it.",
11106
11111
  impact: 5,
11107
11112
  memory_ids: [run.memory_id]
@@ -11795,7 +11800,10 @@ jobs:
11795
11800
  "
11796
11801
 
11797
11802
  Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **" +
11798
- ($receipt.previous_total|tostring) + "**."
11803
+ ($receipt.previous_total|tostring) + "**." +
11804
+ "
11805
+
11806
+ <sub>\u{1F6E1}\uFE0F Generated by [Hivelore](https://github.com/Doucs91/hivelore) \u2014 the deterministic policy gate for agent-written code.</sub>"
11799
11807
  ')" || exit 0
11800
11808
  comments="$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --paginate 2>/dev/null)" || exit 0
11801
11809
  comment_id="$(printf '%s' "$comments" | jq -r '.[] | select(.body | contains("<!-- haive:prevention-receipt -->")) | .id' | head -1)"
@@ -11810,6 +11818,30 @@ Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **
11810
11818
  # haive:enforcement-workflow:end
11811
11819
  `;
11812
11820
  }
11821
+ var CONTENT_CATCH_CODES = /* @__PURE__ */ new Set(["sensor-block", "precommit-policy-block"]);
11822
+ var SETUP_GATE_CODES = /* @__PURE__ */ new Set([
11823
+ "briefing-missing",
11824
+ "session-recap-missing",
11825
+ "decision-coverage-missing",
11826
+ "bootstrap-incomplete",
11827
+ "enforcement-score-below-threshold"
11828
+ ]);
11829
+ function printBlockHeadline(report) {
11830
+ const blocking = report.categories?.blocking ?? report.findings.filter((f) => f.severity === "error");
11831
+ if (blocking.length === 0) return;
11832
+ const catches = blocking.filter((f) => CONTENT_CATCH_CODES.has(f.code));
11833
+ console.log();
11834
+ if (catches.length > 0) {
11835
+ console.log(ui.red(ui.bold("\u{1F6E1}\uFE0F A documented lesson refused this commit \u2014 about the change you just made:")));
11836
+ for (const c of catches) {
11837
+ const id = c.memory_ids?.[0] ?? c.code;
11838
+ console.log(` ${ui.red("\u2022")} ${ui.bold(id)} ${c.message.split("\n")[0]}`);
11839
+ }
11840
+ } else if (blocking.every((f) => SETUP_GATE_CODES.has(f.code))) {
11841
+ console.log(ui.yellow(ui.bold("\u2699 Setup gate \u2014 about your repo's baseline, not the change you just made.")));
11842
+ console.log(ui.dim(" Fill the knowledge layer once (bootstrap / load a briefing); later commits pass silently."));
11843
+ }
11844
+ }
11813
11845
  function printReport(report, json, explain = false) {
11814
11846
  if (json) {
11815
11847
  console.log(JSON.stringify(report, null, 2));
@@ -11818,6 +11850,7 @@ function printReport(report, json, explain = false) {
11818
11850
  console.log(ui.bold(`Hivelore enforcement \u2014 ${report.mode}${report.actor ? ` \xB7 ${report.actor}` : ""}`));
11819
11851
  console.log(ui.dim(` root: ${report.root}`));
11820
11852
  console.log(ui.dim(` score: ${report.score.score}% / threshold ${report.score.threshold}%`));
11853
+ if (report.should_block) printBlockHeadline(report);
11821
11854
  if (explain) {
11822
11855
  printFindingGroup("Blocking", report.categories.blocking, "error");
11823
11856
  printFindingGroup("Review", report.categories.review, "warn");
@@ -12364,7 +12397,7 @@ function registerSensors(program2) {
12364
12397
  });
12365
12398
  sensors.command("propose").description(
12366
12399
  "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 })'"
12367
- ).argument("<memory-id>", "memory id to attach the sensor to").option("--kind <kind>", "regex (default) | shell | test \u2014 command kinds route the team's own oracle to this lesson", "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("--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) => {
12400
+ ).argument("<memory-id>", "memory id to attach the sensor to").option("--kind <kind>", "regex (default) | shell | test \u2014 command kinds route the team's own oracle to this lesson", "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("--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) => {
12368
12401
  if (opts.kind === "shell" || opts.kind === "test") {
12369
12402
  if (!opts.command?.trim()) {
12370
12403
  ui.error("--kind shell|test requires --command.");
@@ -12372,7 +12405,7 @@ function registerSensors(program2) {
12372
12405
  return;
12373
12406
  }
12374
12407
  const root2 = findProjectRoot39(opts.dir);
12375
- const { proposeSensor } = await import("./server-W5Q35K7Q.js");
12408
+ const { proposeSensor } = await import("./server-JFLUYWUB.js");
12376
12409
  const out = await proposeSensor(
12377
12410
  {
12378
12411
  memory_id: id,
@@ -12384,6 +12417,7 @@ function registerSensors(program2) {
12384
12417
  bad_example: void 0,
12385
12418
  severity: opts.severity === "warn" ? "warn" : "block",
12386
12419
  message: opts.message,
12420
+ incident: opts.incident,
12387
12421
  flags: void 0,
12388
12422
  paths: opts.paths ? opts.paths.split(",").map((p) => p.trim()).filter(Boolean) : []
12389
12423
  },
@@ -12435,6 +12469,7 @@ function registerSensors(program2) {
12435
12469
  ...opts.flags ? { flags: opts.flags } : {},
12436
12470
  paths: anchorPaths,
12437
12471
  message: opts.message?.trim() || deriveProposedMessage(found.memory.body, opts.pattern, opts.absent),
12472
+ ...opts.incident?.trim() ? { incident: opts.incident.trim() } : {},
12438
12473
  severity,
12439
12474
  autogen: false,
12440
12475
  last_fired: null
@@ -13209,7 +13244,7 @@ function registerBridges(program2) {
13209
13244
 
13210
13245
  // src/index.ts
13211
13246
  var program = new Command48();
13212
- program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.35.1").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
13247
+ program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.36.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
13213
13248
  registerInit(program);
13214
13249
  registerResolveProject(program);
13215
13250
  registerEnforce(program);