@hivelore/cli 0.49.0 → 0.52.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
@@ -13,7 +13,7 @@ import {
13
13
  readPresumedCorrectTargets,
14
14
  runAstSensorOnContent,
15
15
  runHaiveMcpStdio
16
- } from "./chunk-VW4U72ET.js";
16
+ } from "./chunk-IICT42WP.js";
17
17
  import {
18
18
  registerMemoryPending
19
19
  } from "./chunk-OYJKHD22.js";
@@ -588,7 +588,7 @@ async function applyAutopilotRepairs(root, paths, options = {}) {
588
588
  if (changed) {
589
589
  repairs.push({
590
590
  code: "autopilot-config",
591
- message: "Enabled autopilot defaults in .ai/haive.config.json."
591
+ message: "Enabled autopilot defaults in .ai/hivelore.config.json."
592
592
  });
593
593
  }
594
594
  }
@@ -1483,7 +1483,7 @@ function isCodeMapStale(root, generatedAt, files) {
1483
1483
 
1484
1484
  // src/commands/init.ts
1485
1485
  import { execFile as execFile2 } from "child_process";
1486
- import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
1486
+ import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, rm, writeFile as writeFile7 } from "fs/promises";
1487
1487
  import { existsSync as existsSync12 } from "fs";
1488
1488
  import path13 from "path";
1489
1489
  import { spawnSync as spawnSync3 } from "child_process";
@@ -1491,6 +1491,10 @@ import { promisify as promisify2 } from "util";
1491
1491
  import "commander";
1492
1492
  import {
1493
1493
  AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
1494
+ CONFIG_FILE,
1495
+ LEGACY_CONFIG_FILE,
1496
+ loadConfig as loadConfig3,
1497
+ resolveConfigPath,
1494
1498
  BRIDGE_TARGETS as BRIDGE_TARGETS2,
1495
1499
  buildCodeMap as buildCodeMap3,
1496
1500
  buildFrontmatter as buildFrontmatter2,
@@ -3758,7 +3762,7 @@ ${SEED_FOOTER(stack)}` });
3758
3762
 
3759
3763
  // src/commands/init.ts
3760
3764
  var execFileAsync = promisify2(execFile2);
3761
- var HAIVE_GITHUB_ACTION_REF = `v${"0.49.0"}`;
3765
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.52.0"}`;
3762
3766
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3763
3767
 
3764
3768
  > Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
@@ -3804,7 +3808,7 @@ This repository uses **Hivelore**. Running \`hivelore init\` means the team expe
3804
3808
 
3805
3809
  Tell the developer to enable the **hivelore** server (e.g. \`hivelore mcp --stdio\` in client config) and restart the client. Do not silently ignore Hivelore.
3806
3810
  `;
3807
- var CI_WORKFLOW = `name: haive-sync
3811
+ var CI_WORKFLOW = `name: hivelore-sync
3808
3812
 
3809
3813
  on:
3810
3814
  push:
@@ -3926,7 +3930,7 @@ jobs:
3926
3930
  run: hivelore eval --trend || true
3927
3931
 
3928
3932
  # On push to main: push shared memories to the hub (if hubPath is configured)
3929
- # Uncomment and configure hubPath in .ai/haive.config.json to enable.
3933
+ # Uncomment and configure hubPath in .ai/hivelore.config.json to enable.
3930
3934
  # hub-push:
3931
3935
  # if: github.event_name == 'push'
3932
3936
  # needs: sync-on-merge
@@ -3944,7 +3948,7 @@ jobs:
3944
3948
  # run: npm install -g @hivelore/cli
3945
3949
  # - name: push shared memories to hub
3946
3950
  # run: hivelore hub push --commit
3947
- # # Requires hubPath in .ai/haive.config.json pointing to a cloned hub repo.
3951
+ # # Requires hubPath in .ai/hivelore.config.json pointing to a cloned hub repo.
3948
3952
  `;
3949
3953
  function registerInit(program2) {
3950
3954
  program2.command("init").description(
@@ -3954,7 +3958,7 @@ function registerInit(program2) {
3954
3958
  `which agent bridges to generate: 'auto' (default \u2014 clients detected on this machine/repo, plus AGENTS.md) | 'all' | comma-list.
3955
3959
  Available: ${BRIDGE_TARGETS2.join(", ")}. Each carries top memories + block sensors.`,
3956
3960
  "auto"
3957
- ).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/haive-sync.yml) \u2014 included automatically in autopilot mode").option(
3961
+ ).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/hivelore-sync.yml) \u2014 included automatically in autopilot mode").option(
3958
3962
  "--manual",
3959
3963
  "opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
3960
3964
  ).option(
@@ -4031,14 +4035,16 @@ function registerInit(program2) {
4031
4035
  ui.success(`Created ${path13.relative(root, paths.projectContext)}`);
4032
4036
  }
4033
4037
  }
4034
- const configExists = existsSync12(
4035
- path13.join(paths.haiveDir, "haive.config.json")
4036
- );
4038
+ const resolvedConfig = resolveConfigPath(paths);
4039
+ const configExists = existsSync12(resolvedConfig);
4037
4040
  if (!configExists) {
4038
4041
  await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
4039
4042
  ui.success(
4040
- `Created .ai/haive.config.json (mode: ${autopilot ? "autopilot" : "standard"})`
4043
+ `Created .ai/${CONFIG_FILE} (mode: ${autopilot ? "autopilot" : "standard"})`
4041
4044
  );
4045
+ } else if (!resolvedConfig.endsWith(CONFIG_FILE)) {
4046
+ await saveConfig2(paths, await loadConfig3(paths));
4047
+ ui.info(`Migrated .ai/${LEGACY_CONFIG_FILE} \u2192 .ai/${CONFIG_FILE}.`);
4042
4048
  }
4043
4049
  const stacksToSeed = await resolveStacksToSeed(root, wantStack);
4044
4050
  if (stacksToSeed.length > 0) {
@@ -4102,13 +4108,19 @@ function registerInit(program2) {
4102
4108
  }
4103
4109
  const wantCi = opts.withCi || autopilot;
4104
4110
  if (wantCi) {
4105
- const ciPath = path13.join(root, ".github", "workflows", "haive-sync.yml");
4111
+ const workflowsDir = path13.join(root, ".github", "workflows");
4112
+ const ciPath = path13.join(workflowsDir, "hivelore-sync.yml");
4113
+ const legacyCiPath = path13.join(workflowsDir, "haive-sync.yml");
4106
4114
  if (existsSync12(ciPath)) {
4107
4115
  ui.info("CI workflow already exists \u2014 skipped");
4108
4116
  } else {
4109
- await mkdir6(path13.dirname(ciPath), { recursive: true });
4117
+ await mkdir6(workflowsDir, { recursive: true });
4110
4118
  await writeFile7(ciPath, CI_WORKFLOW, "utf8");
4111
4119
  ui.success(`Created ${path13.relative(root, ciPath)}`);
4120
+ if (existsSync12(legacyCiPath)) {
4121
+ await rm(legacyCiPath, { force: true });
4122
+ ui.info(`Migrated ${path13.relative(root, legacyCiPath)} \u2192 ${path13.relative(root, ciPath)}.`);
4123
+ }
4112
4124
  }
4113
4125
  }
4114
4126
  if (autopilot) {
@@ -4733,7 +4745,7 @@ import {
4733
4745
  isDecaying,
4734
4746
  isStackPackSeed,
4735
4747
  loadCodeMap as loadCodeMap5,
4736
- loadConfig as loadConfig3,
4748
+ loadConfig as loadConfig4,
4737
4749
  loadMemoriesFromDir as loadMemoriesFromDir7,
4738
4750
  loadSensorLedger,
4739
4751
  loadUsageIndex as loadUsageIndex3,
@@ -4771,7 +4783,7 @@ function registerSync(program2) {
4771
4783
  const log = (msg) => {
4772
4784
  if (!opts.quiet) console.log(msg);
4773
4785
  };
4774
- const config = await loadConfig3(paths);
4786
+ const config = await loadConfig4(paths);
4775
4787
  const autoApproveDelayHours = config.autoApproveDelayHours ?? null;
4776
4788
  const autoPromoteMinReads = config.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads;
4777
4789
  const autoRepair = config.autoRepair ?? {};
@@ -5437,7 +5449,7 @@ import {
5437
5449
  suggestSensorSeed,
5438
5450
  findProjectRoot as findProjectRoot10,
5439
5451
  inferModulesFromPaths as inferModulesFromPaths2,
5440
- loadConfig as loadConfig4,
5452
+ loadConfig as loadConfig5,
5441
5453
  loadMemoriesFromDir as loadMemoriesFromDir8,
5442
5454
  memoryFilePath as memoryFilePath3,
5443
5455
  resolveHaivePaths as resolveHaivePaths9,
@@ -5477,7 +5489,7 @@ function registerMemoryAdd(memory2) {
5477
5489
  process.exitCode = 1;
5478
5490
  return;
5479
5491
  }
5480
- const config = await loadConfig4(paths);
5492
+ const config = await loadConfig5(paths);
5481
5493
  const userTags = parseCsv2(opts.tags);
5482
5494
  const anchorPaths = parseCsv2(opts.paths ?? opts.files);
5483
5495
  const activation = opts.type === "skill" && (opts.activationKeyword || opts.activationGlob || opts.activationAlways) ? {
@@ -6129,7 +6141,7 @@ import path22 from "path";
6129
6141
  import "commander";
6130
6142
  import {
6131
6143
  findProjectRoot as findProjectRoot16,
6132
- loadConfig as loadConfig5,
6144
+ loadConfig as loadConfig6,
6133
6145
  resolveHaivePaths as resolveHaivePaths15
6134
6146
  } from "@hivelore/core";
6135
6147
  async function readDependencyMap(root) {
@@ -6210,7 +6222,7 @@ function registerMemorySeed(memory2) {
6210
6222
  ui.success(`Seeded ${total} starter memor${total === 1 ? "y" : "ies"}: ${seededStacks.join(", ")}`);
6211
6223
  ui.info("Kept at background priority. Anchor them to a real file (or replace them) to make them high-signal:");
6212
6224
  ui.info(" hivelore memory update <id> --paths <key-file> # anchor a seed to a file");
6213
- const config = await loadConfig5(paths);
6225
+ const config = await loadConfig6(paths);
6214
6226
  if (config.autopilot || config.autoRepair?.corpus === true) {
6215
6227
  const repairs = await applyAutopilotRepairs(root, paths, {
6216
6228
  applyConfig: false,
@@ -6679,7 +6691,7 @@ import {
6679
6691
  } from "@hivelore/core";
6680
6692
  function registerMemoryVerify(memory2) {
6681
6693
  memory2.command("verify").description(
6682
- "Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n hivelore sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'hivelore memory verify' to your haive-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n hivelore memory verify # check all, report only\n hivelore memory verify --update # mark stale/fresh on disk\n hivelore memory verify --id 2026-04-28-gotcha-x # check one memory\n"
6694
+ "Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n hivelore sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'hivelore memory verify' to your hivelore-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n hivelore memory verify # check all, report only\n hivelore memory verify --update # mark stale/fresh on disk\n hivelore memory verify --id 2026-04-28-gotcha-x # check one memory\n"
6683
6695
  ).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
6684
6696
  const root = findProjectRoot24(opts.dir);
6685
6697
  const paths = resolveHaivePaths23(root);
@@ -6966,7 +6978,7 @@ function registerMemoryDigest(program2) {
6966
6978
  }
6967
6979
 
6968
6980
  // src/commands/session-end.ts
6969
- import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm } from "fs/promises";
6981
+ import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm as rm2 } from "fs/promises";
6970
6982
  import { existsSync as existsSync32 } from "fs";
6971
6983
  import { spawn as spawn2 } from "child_process";
6972
6984
  import path29 from "path";
@@ -6975,7 +6987,7 @@ import {
6975
6987
  distillFailureObservations,
6976
6988
  buildFrontmatter as buildFrontmatter5,
6977
6989
  findProjectRoot as findProjectRoot27,
6978
- loadConfig as loadConfig6,
6990
+ loadConfig as loadConfig7,
6979
6991
  loadMemoriesFromDir as loadMemoriesFromDir10,
6980
6992
  loadPreventionEvents,
6981
6993
  readUsageEvents,
@@ -7328,10 +7340,10 @@ ${note}` : note;
7328
7340
  const cleanupObservations = async () => {
7329
7341
  if (!opts.auto) return;
7330
7342
  const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
7331
- if (existsSync32(obsFile)) await rm(obsFile).catch(() => {
7343
+ if (existsSync32(obsFile)) await rm2(obsFile).catch(() => {
7332
7344
  });
7333
7345
  };
7334
- const config = await loadConfig6(paths);
7346
+ const config = await loadConfig7(paths);
7335
7347
  if (opts.auto && config.autoSessionRecap === false) {
7336
7348
  if (config.sessionHandoff) {
7337
7349
  const diffStat = await runGit(root, ["diff", "--stat", "HEAD"]).catch(() => "");
@@ -7958,7 +7970,7 @@ import {
7958
7970
  computeGatePrecision,
7959
7971
  computeEvalTrend,
7960
7972
  findProjectRoot as findProjectRoot31,
7961
- loadConfig as loadConfig7,
7973
+ loadConfig as loadConfig8,
7962
7974
  loadEvalHistory,
7963
7975
  loadPreventionEvents as loadPreventionEvents3,
7964
7976
  loadUsageIndex as loadUsageIndex13,
@@ -8065,7 +8077,7 @@ function registerEval(program2) {
8065
8077
  const [usage, preventionEvents, config] = await Promise.all([
8066
8078
  loadUsageIndex13(paths),
8067
8079
  loadPreventionEvents3(paths),
8068
- loadConfig7(paths)
8080
+ loadConfig8(paths)
8069
8081
  ]);
8070
8082
  const gatePrecision = computeGatePrecision(
8071
8083
  preventionEvents,
@@ -8393,7 +8405,7 @@ import {
8393
8405
  aggregateUsage as aggregateUsage2,
8394
8406
  buildFrontmatter as buildFrontmatter6,
8395
8407
  findProjectRoot as findProjectRoot32,
8396
- loadConfig as loadConfig8,
8408
+ loadConfig as loadConfig9,
8397
8409
  loadMemoriesFromDir as loadMemoriesFromDir12,
8398
8410
  memoryFilePath as memoryFilePath6,
8399
8411
  parseSince as parseSince2,
@@ -8465,7 +8477,7 @@ function registerMemorySuggest(memory2) {
8465
8477
  inferred_type: inferType(v.tools, query)
8466
8478
  })).sort((a, b) => b.count - a.count);
8467
8479
  if (opts.autoSave) {
8468
- const config = await loadConfig8(paths);
8480
+ const config = await loadConfig9(paths);
8469
8481
  const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
8470
8482
  const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
8471
8483
  const status = config.defaultStatus === "validated" ? "validated" : "draft";
@@ -8684,7 +8696,7 @@ import {
8684
8696
  findProjectRoot as findProjectRoot34,
8685
8697
  getUsage as getUsage8,
8686
8698
  retirementSignal as retirementSignal2,
8687
- loadConfig as loadConfig9,
8699
+ loadConfig as loadConfig10,
8688
8700
  loadMemoriesFromDir as loadMemoriesFromDir13,
8689
8701
  loadUsageIndex as loadUsageIndex14,
8690
8702
  resolveHaivePaths as resolveHaivePaths32,
@@ -8702,7 +8714,7 @@ function registerMemoryArchive(memory2) {
8702
8714
  process.exitCode = 1;
8703
8715
  return;
8704
8716
  }
8705
- const config = await loadConfig9(paths);
8717
+ const config = await loadConfig10(paths);
8706
8718
  const defaultWindow = config.enforcement?.decayAfterDays ? `${config.enforcement.decayAfterDays}d` : "180d";
8707
8719
  const minDays = parseDays(opts.since ?? defaultWindow);
8708
8720
  if (minDays === null) {
@@ -8816,7 +8828,7 @@ import {
8816
8828
  getUsage as getUsage9,
8817
8829
  isStackPackSeed as isStackPackSeed2,
8818
8830
  loadCodeMap as loadCodeMap7,
8819
- loadConfig as loadConfig10,
8831
+ loadConfig as loadConfig11,
8820
8832
  loadMemoriesFromDirDetailed,
8821
8833
  loadSensorLedger as loadSensorLedger2,
8822
8834
  loadUsageIndex as loadUsageIndex15,
@@ -8906,12 +8918,12 @@ var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
8906
8918
  function registerDoctor(program2) {
8907
8919
  program2.command("doctor").description(
8908
8920
  "Analyze the local Hivelore setup and emit actionable recommendations.\n\n Inspects: project-context status, memory health (stale/anchorless/decay/pending),\n code-map freshness, usage log signals (low-hit briefings, repeated empty searches).\n\n Read-only by default. Pass --fix to apply safe autopilot repairs."
8909
- ).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("--dry-run", "with --fix, show delegated repairs without applying them", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
8921
+ ).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("--dry-run", "with --fix, show delegated repairs without applying them", false).option("--all", "show informational findings too (hidden by default to keep doctor actionable)", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
8910
8922
  const root = findProjectRoot35(opts.dir);
8911
8923
  const paths = resolveHaivePaths33(root);
8912
8924
  const findings = [];
8913
8925
  const repairs = [];
8914
- const config = await loadConfig10(paths);
8926
+ const config = await loadConfig11(paths);
8915
8927
  if (!existsSync40(paths.haiveDir)) {
8916
8928
  if (opts.json) {
8917
8929
  console.log(JSON.stringify({
@@ -9146,7 +9158,7 @@ function registerDoctor(program2) {
9146
9158
  }
9147
9159
  const astSensorCount = sensorMemories.filter((m) => m.memory.frontmatter.sensor?.kind === "ast").length;
9148
9160
  if (astSensorCount > 0) {
9149
- const { astEngineAvailable: astEngineAvailable2 } = await import("./server-ZE5Y3Z4A.js");
9161
+ const { astEngineAvailable: astEngineAvailable2 } = await import("./server-UAD33QQZ.js");
9150
9162
  if (!await astEngineAvailable2()) {
9151
9163
  findings.push({
9152
9164
  severity: "warn",
@@ -9314,11 +9326,11 @@ function registerDoctor(program2) {
9314
9326
  severity: "info",
9315
9327
  code: "no-autopilot",
9316
9328
  message: "Autopilot is OFF \u2014 session recaps are not auto-saved on shutdown.",
9317
- fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
9329
+ fix: "Edit .ai/hivelore.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
9318
9330
  });
9319
9331
  }
9320
- findings.push(...await collectInstallFindings(root, "0.49.0"));
9321
- findings.push(...await collectMcpRuntimeFindings(paths, "0.49.0"));
9332
+ findings.push(...await collectInstallFindings(root, "0.52.0"));
9333
+ findings.push(...await collectMcpRuntimeFindings(paths, "0.52.0"));
9322
9334
  findings.push(...await collectToolchainFindings(root));
9323
9335
  try {
9324
9336
  const legacyRaw = execSync("haive-mcp --version", {
@@ -9326,7 +9338,7 @@ function registerDoctor(program2) {
9326
9338
  timeout: 3e3,
9327
9339
  stdio: ["ignore", "pipe", "ignore"]
9328
9340
  }).trim();
9329
- const cliVersion = "0.49.0";
9341
+ const cliVersion = "0.52.0";
9330
9342
  if (legacyRaw && legacyRaw !== cliVersion) {
9331
9343
  findings.push({
9332
9344
  severity: "warn",
@@ -9453,12 +9465,16 @@ function emit(findings, opts, repairs = []) {
9453
9465
  "Next actions"
9454
9466
  ];
9455
9467
  const severityOrder = ["error", "warn", "info"];
9468
+ const isVisible = (f) => f.severity !== "info" || opts.all === true || f.alwaysShow === true;
9469
+ let hiddenInfo = 0;
9456
9470
  for (const section2 of sectionOrder) {
9457
9471
  const sectionFindings = classified.filter((f) => f.section === section2);
9458
- if (sectionFindings.length === 0) continue;
9472
+ const visible = sectionFindings.filter(isVisible);
9473
+ hiddenInfo += sectionFindings.length - visible.length;
9474
+ if (visible.length === 0) continue;
9459
9475
  console.log(ui.bold(section2));
9460
9476
  for (const sev of severityOrder) {
9461
- for (const f of sectionFindings.filter((x) => x.severity === sev)) {
9477
+ for (const f of visible.filter((x) => x.severity === sev)) {
9462
9478
  const icon = sev === "error" ? ui.red("\u2717") : sev === "warn" ? ui.yellow("\u26A0") : ui.dim("\u2139");
9463
9479
  console.log(`${icon} ${ui.bold(f.code)} ${f.message}`);
9464
9480
  if (opts.fix && f.fix) {
@@ -9470,6 +9486,9 @@ function emit(findings, opts, repairs = []) {
9470
9486
  }
9471
9487
  console.log();
9472
9488
  }
9489
+ if (hiddenInfo > 0) {
9490
+ ui.info(`${hiddenInfo} informational finding(s) hidden \u2014 run \`hivelore doctor --all\` to show them.`);
9491
+ }
9473
9492
  if (repairs.length > 0) {
9474
9493
  console.log(ui.bold("Autopilot repairs applied"));
9475
9494
  for (const repair of repairs) console.log(` ${ui.dim("\u2713")} ${repair.message}`);
@@ -9576,6 +9595,7 @@ async function collectHarnessCoverageFindings(codeMap, memories) {
9576
9595
  findings.push({
9577
9596
  severity: "info",
9578
9597
  code: "harness-coverage",
9598
+ alwaysShow: true,
9579
9599
  coverage_percent: pct2,
9580
9600
  message: `${covered}/${total} code-map files have validated memory anchors (${pct2}%). ` + coverageDesc + uncoveredHint,
9581
9601
  fix: pct2 < 50 && total > 10 ? `hivelore memory save --type gotcha|convention|architecture --paths <key-file> --scope team` : void 0,
@@ -9587,7 +9607,7 @@ function collectBehaviourCoverageFindings(codeMap, memories) {
9587
9607
  if (!codeMap) return [];
9588
9608
  const codeFiles = Object.keys(codeMap.files);
9589
9609
  const cov = assessBehaviourCoverage2({ memories, codeFiles });
9590
- if (cov.mainAreas.length === 0) return [];
9610
+ if (cov.mainAreas.length === 0 && cov.totalOracles === 0) return [];
9591
9611
  const uncoveredHint = cov.uncoveredAreas.length > 0 && cov.totalOracles > 0 ? `
9592
9612
  No behavioural oracle: ${cov.uncoveredAreas.slice(0, 5).map((a) => `\`${a}\``).join(", ")}` + (cov.uncoveredAreas.length > 5 ? `, +${cov.uncoveredAreas.length - 5} more` : "") : "";
9593
9613
  let fix;
@@ -9603,8 +9623,9 @@ function collectBehaviourCoverageFindings(codeMap, memories) {
9603
9623
  return [{
9604
9624
  severity: "info",
9605
9625
  code: "behaviour-coverage",
9626
+ alwaysShow: true,
9606
9627
  section: "Protection",
9607
- coverage_percent: Math.round(cov.areasWithOracle.length / cov.mainAreas.length * 100),
9628
+ coverage_percent: cov.mainAreas.length > 0 ? Math.round(cov.areasWithOracle.length / cov.mainAreas.length * 100) : cov.totalOracles > 0 ? 100 : 0,
9608
9629
  message: `Behaviour harness: ${renderBehaviourCoverageLine2(cov)}.` + uncoveredHint,
9609
9630
  fix
9610
9631
  }];
@@ -9955,7 +9976,7 @@ import "commander";
9955
9976
  import {
9956
9977
  antiPatternGateParams,
9957
9978
  findProjectRoot as findProjectRoot36,
9958
- loadConfig as loadConfig11,
9979
+ loadConfig as loadConfig12,
9959
9980
  resolveHaivePaths as resolveHaivePaths34
9960
9981
  } from "@hivelore/core";
9961
9982
  function registerPrecommit(program2) {
@@ -9971,7 +9992,7 @@ function registerPrecommit(program2) {
9971
9992
  const root = findProjectRoot36(opts.dir);
9972
9993
  const paths = resolveHaivePaths34(root);
9973
9994
  const ctx = { paths };
9974
- const config = await loadConfig11(paths);
9995
+ const config = await loadConfig12(paths);
9975
9996
  const gate = config.enforcement?.antiPatternGate ?? "anchored";
9976
9997
  const gateParams = antiPatternGateParams(gate);
9977
9998
  const blockOn = opts.blockOn ?? gateParams.block_on;
@@ -10120,7 +10141,7 @@ function registerResolveProject(program2) {
10120
10141
  // src/commands/enforce.ts
10121
10142
  import { execFile as execFile5, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
10122
10143
  import { existsSync as existsSync42, statSync as statSync4 } from "fs";
10123
- import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as rm2, writeFile as writeFile25 } from "fs/promises";
10144
+ import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as rm3, writeFile as writeFile25 } from "fs/promises";
10124
10145
  import path40 from "path";
10125
10146
  import { promisify as promisify5 } from "util";
10126
10147
  import "commander";
@@ -10141,7 +10162,7 @@ import {
10141
10162
  hasRecentBriefingMarker,
10142
10163
  isFreshIsoDate,
10143
10164
  isRetiredMemory as isRetiredMemory2,
10144
- loadConfig as loadConfig12,
10165
+ loadConfig as loadConfig13,
10145
10166
  detectAgentContext,
10146
10167
  loadMemoriesFromDir as loadMemoriesFromDir15,
10147
10168
  loadSensorLedger as loadSensorLedger3,
@@ -10439,7 +10460,7 @@ function registerEnforce(program2) {
10439
10460
  const root = findProjectRoot37(opts.dir);
10440
10461
  const paths = resolveHaivePaths35(root);
10441
10462
  await mkdir16(paths.haiveDir, { recursive: true });
10442
- const current = await loadConfig12(paths);
10463
+ const current = await loadConfig13(paths);
10443
10464
  await saveConfig3(paths, {
10444
10465
  ...current,
10445
10466
  enforcement: {
@@ -10456,7 +10477,7 @@ function registerEnforce(program2) {
10456
10477
  policyPacks: ["architecture", "gotchas", "security", "domain", "release"]
10457
10478
  }
10458
10479
  });
10459
- ui.success("Hivelore strict enforcement enabled in .ai/haive.config.json");
10480
+ ui.success("Hivelore strict enforcement enabled in .ai/hivelore.config.json");
10460
10481
  if (opts.git !== false) await installGitEnforcement(root);
10461
10482
  if (opts.ci !== false) await installCiEnforcement(root);
10462
10483
  if (opts.claude !== false) {
@@ -10611,7 +10632,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
10611
10632
  const paths = resolveHaivePaths35(root);
10612
10633
  if (!existsSync42(paths.haiveDir)) return;
10613
10634
  if (!isWriteLikeTool(payload)) return;
10614
- const config = await loadConfig12(paths);
10635
+ const config = await loadConfig13(paths);
10615
10636
  if (config.enforcement?.requireBriefingFirst === false) return;
10616
10637
  const gate = config.enforcement?.preEditGate ?? "advise";
10617
10638
  const targetFiles = extractToolPaths(payload, root);
@@ -10625,7 +10646,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
10625
10646
  const contextText = buildPreEditContext(payload.tool_name ?? "write tool", targetFiles, missing, hasMarker);
10626
10647
  if (gate === "block") {
10627
10648
  console.error(
10628
- contextText + '\n\nThe relevant context is now recorded \u2014 re-issue the same edit to proceed (no `hivelore briefing` command needed). To make this advisory instead of blocking, set `{ "enforcement": { "preEditGate": "advise" } }` in .ai/haive.config.json.'
10649
+ contextText + '\n\nThe relevant context is now recorded \u2014 re-issue the same edit to proceed (no `hivelore briefing` command needed). To make this advisory instead of blocking, set `{ "enforcement": { "preEditGate": "advise" } }` in .ai/hivelore.config.json.'
10629
10650
  );
10630
10651
  process.exit(2);
10631
10652
  }
@@ -10691,7 +10712,7 @@ async function buildFinishReport(dir) {
10691
10712
  const root = findProjectRoot37(dir);
10692
10713
  const paths = resolveHaivePaths35(root);
10693
10714
  const initialized = existsSync42(paths.haiveDir);
10694
- const config = initialized ? await loadConfig12(paths) : {};
10715
+ const config = initialized ? await loadConfig13(paths) : {};
10695
10716
  const mode = config.enforcement?.mode ?? "strict";
10696
10717
  const findings = [];
10697
10718
  if (!initialized) {
@@ -10970,7 +10991,7 @@ async function runWithEnforcement(command, args, opts) {
10970
10991
  HAIVE_ENFORCEMENT: "strict",
10971
10992
  HAIVE_AGENT: "1",
10972
10993
  // wrapped process is an agent — process gates bind it (detectAgentContext)
10973
- HAIVE_TOOL_PROFILE: process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
10994
+ HIVELORE_TOOL_PROFILE: process.env.HIVELORE_TOOL_PROFILE ?? process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
10974
10995
  }
10975
10996
  });
10976
10997
  await new Promise((resolve, reject) => {
@@ -11100,7 +11121,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11100
11121
  const root = findProjectRoot37(dir);
11101
11122
  const paths = resolveHaivePaths35(root);
11102
11123
  const initialized = existsSync42(paths.haiveDir);
11103
- const config = initialized ? await loadConfig12(paths) : {};
11124
+ const config = initialized ? await loadConfig13(paths) : {};
11104
11125
  if (initialized) {
11105
11126
  await applyLightweightRepairs(root, paths);
11106
11127
  if (stage === "pre-commit") await stageResyncedArtifacts(root, paths);
@@ -11133,7 +11154,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11133
11154
  findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
11134
11155
  });
11135
11156
  }
11136
- findings.push(...await inspectIntegrationVersions(root, "0.49.0"));
11157
+ findings.push(...await inspectIntegrationVersions(root, "0.52.0"));
11137
11158
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
11138
11159
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
11139
11160
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
@@ -11344,7 +11365,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
11344
11365
  }];
11345
11366
  }
11346
11367
  if (stage === "pre-commit" || stage === "pre-push") {
11347
- const cfg = await loadConfig12(paths).catch(() => ({}));
11368
+ const cfg = await loadConfig13(paths).catch(() => ({}));
11348
11369
  if (cfg.enforcement?.autoBrief !== false) {
11349
11370
  await writeBriefingMarker2(paths, {
11350
11371
  sessionId,
@@ -11417,9 +11438,8 @@ async function runPrecommitPolicy(paths, gate, stage, config) {
11417
11438
  semantic: true
11418
11439
  }, { paths });
11419
11440
  const sensorFindings = await runSensorGate(paths, snapshot.diff, stage);
11420
- const reviewWarnings = result.warnings.filter(
11421
- (w) => w.level === "review" && !w.reasons.includes("sensor")
11422
- );
11441
+ const showReview = config.enforcement?.reviewMatches === true || gate === "review";
11442
+ const reviewWarnings = showReview ? result.warnings.filter((w) => w.level === "review" && !w.reasons.includes("sensor")) : [];
11423
11443
  const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
11424
11444
  const reviewSeenFile = path40.join(paths.runtimeDir, "enforcement", "review-seen.json");
11425
11445
  let reviewSeen = {};
@@ -11640,7 +11660,7 @@ async function runSensorGate(paths, diff, stage) {
11640
11660
  }
11641
11661
  }
11642
11662
  }
11643
- const config = await loadConfig12(paths).catch(() => ({}));
11663
+ const config = await loadConfig13(paths).catch(() => ({}));
11644
11664
  if (config?.enforcement?.runCommandSensors === true) {
11645
11665
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
11646
11666
  const specs = selectCommandSensors(scannable, changedPaths).filter((sp) => !seen.has(sp.memory_id));
@@ -11725,12 +11745,13 @@ command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlo
11725
11745
  }
11726
11746
  return findings;
11727
11747
  } catch (err) {
11748
+ const inCi = stage === "ci";
11728
11749
  return [{
11729
- severity: "warn",
11750
+ severity: inCi ? "error" : "warn",
11730
11751
  code: "sensor-gate-errored",
11731
- message: `The sensor gate itself errored, so NO sensors were evaluated on this diff: ` + `${err instanceof Error ? err.message : String(err)}`.slice(0, 400),
11732
- fix: "Run `hivelore sensors check` to reproduce, and `hivelore doctor` for setup drift. The lessons' protection is OFF until this is fixed.",
11733
- impact: 5
11752
+ message: `\u26D4 The sensor gate itself errored, so NO sensors were evaluated on this diff \u2014 the deterministic protection was OFF for this run${inCi ? " (failing CI: a passing gate that evaluated nothing is not trustworthy)" : ""}: ` + `${err instanceof Error ? err.message : String(err)}`.slice(0, 500),
11753
+ fix: "Run `hivelore sensors check` to reproduce (set HIVELORE_DEBUG=1 for the stack), and `hivelore doctor` for setup drift. Protection is OFF until this is fixed.",
11754
+ impact: inCi ? 60 : 25
11734
11755
  }];
11735
11756
  }
11736
11757
  }
@@ -11767,7 +11788,7 @@ async function cleanupRuntimeDir(runtimeDir) {
11767
11788
  removed += await cleanupEnforcementDir(path40.join(runtimeDir, entry.name));
11768
11789
  continue;
11769
11790
  }
11770
- await rm2(path40.join(runtimeDir, entry.name), { recursive: true, force: true });
11791
+ await rm3(path40.join(runtimeDir, entry.name), { recursive: true, force: true });
11771
11792
  removed++;
11772
11793
  }
11773
11794
  await writeFile25(path40.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
@@ -11786,7 +11807,7 @@ async function cleanupCacheDir(cacheDir) {
11786
11807
  const entries = await readdir4(cacheDir, { withFileTypes: true }).catch(() => []);
11787
11808
  for (const entry of entries) {
11788
11809
  if (entry.name === ".gitignore") continue;
11789
- await rm2(path40.join(cacheDir, entry.name), { recursive: true, force: true });
11810
+ await rm3(path40.join(cacheDir, entry.name), { recursive: true, force: true });
11790
11811
  removed++;
11791
11812
  }
11792
11813
  await writeFile25(path40.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
@@ -11797,7 +11818,7 @@ async function cleanupEnforcementDir(enforcementDir) {
11797
11818
  const entries = await readdir4(enforcementDir, { withFileTypes: true }).catch(() => []);
11798
11819
  for (const entry of entries) {
11799
11820
  if (entry.name === "briefings") continue;
11800
- await rm2(path40.join(enforcementDir, entry.name), { recursive: true, force: true });
11821
+ await rm3(path40.join(enforcementDir, entry.name), { recursive: true, force: true });
11801
11822
  removed++;
11802
11823
  }
11803
11824
  return removed;
@@ -11914,8 +11935,8 @@ async function getPolicyDiffSnapshot(root, stage) {
11914
11935
  return { diff: "", paths: [], source: "none" };
11915
11936
  }
11916
11937
  async function resolveCiDiffRange(root) {
11917
- const explicitBase = cleanGitSha(process.env.HAIVE_BASE_SHA ?? process.env.HAIVE_BASE_REF);
11918
- const explicitHead = cleanGitSha(process.env.HAIVE_HEAD_SHA ?? process.env.GITHUB_SHA) ?? "HEAD";
11938
+ const explicitBase = cleanGitSha(process.env.HIVELORE_BASE_SHA ?? process.env.HIVELORE_BASE_REF ?? process.env.HAIVE_BASE_SHA ?? process.env.HAIVE_BASE_REF);
11939
+ const explicitHead = cleanGitSha(process.env.HIVELORE_HEAD_SHA ?? process.env.HAIVE_HEAD_SHA ?? process.env.GITHUB_SHA) ?? "HEAD";
11919
11940
  if (explicitBase && await gitCommitExists(root, explicitBase)) {
11920
11941
  return `${explicitBase}...${explicitHead}`;
11921
11942
  }
@@ -12263,7 +12284,6 @@ async function installGitEnforcement(root) {
12263
12284
  await mkdir16(hooksDir, { recursive: true });
12264
12285
  const resolveCli = `_hivelore() {
12265
12286
  if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
12266
- elif command -v haive >/dev/null 2>&1; then haive "$@"
12267
12287
  else return 0
12268
12288
  fi
12269
12289
  }`;
@@ -12330,20 +12350,30 @@ ${hook.body}`, "utf8");
12330
12350
  ui.success("Installed git hooks: pre-commit, pre-push, commit-msg (blocking) + post-merge, post-rewrite (sync)");
12331
12351
  }
12332
12352
  async function installCiEnforcement(root) {
12333
- const workflowPath = path40.join(root, ".github", "workflows", "haive-enforcement.yml");
12334
- await mkdir16(path40.dirname(workflowPath), { recursive: true });
12353
+ const workflowsDir = path40.join(root, ".github", "workflows");
12354
+ const workflowPath = path40.join(workflowsDir, "hivelore-enforcement.yml");
12355
+ const legacyPath = path40.join(workflowsDir, "haive-enforcement.yml");
12356
+ await mkdir16(workflowsDir, { recursive: true });
12335
12357
  const workflow = renderCiEnforcementWorkflow();
12336
- if (existsSync42(workflowPath)) {
12337
- const existing = await readFile18(workflowPath, "utf8");
12338
- const start = "# haive:enforcement-workflow:start";
12339
- const end = "# haive:enforcement-workflow:end";
12340
- const startAt = existing.indexOf(start);
12341
- const endAt = existing.indexOf(end);
12358
+ const source = existsSync42(workflowPath) ? workflowPath : existsSync42(legacyPath) ? legacyPath : null;
12359
+ if (source) {
12360
+ const existing = await readFile18(source, "utf8");
12361
+ const startAt = Math.max(existing.indexOf("# hivelore:enforcement-workflow:start"), existing.indexOf("# haive:enforcement-workflow:start"));
12362
+ const endMarker = existing.includes("# hivelore:enforcement-workflow:end") ? "# hivelore:enforcement-workflow:end" : "# haive:enforcement-workflow:end";
12363
+ const endAt = existing.indexOf(endMarker);
12342
12364
  if (startAt >= 0 && endAt > startAt) {
12343
- await writeFile25(workflowPath, existing.slice(0, startAt) + workflow + existing.slice(endAt + end.length), "utf8");
12365
+ await writeFile25(workflowPath, existing.slice(0, startAt) + workflow + existing.slice(endAt + endMarker.length), "utf8");
12344
12366
  ui.success(`Updated ${path40.relative(root, workflowPath)} managed block`);
12345
- } else {
12367
+ } else if (source === workflowPath) {
12346
12368
  ui.info("GitHub Actions enforcement workflow already exists without Hivelore markers \u2014 preserved");
12369
+ return;
12370
+ } else {
12371
+ await writeFile25(workflowPath, workflow, "utf8");
12372
+ ui.success(`Created ${path40.relative(root, workflowPath)}`);
12373
+ }
12374
+ if (source === legacyPath) {
12375
+ await rm3(legacyPath, { force: true });
12376
+ ui.info(`Migrated ${path40.relative(root, legacyPath)} \u2192 ${path40.relative(root, workflowPath)}.`);
12347
12377
  }
12348
12378
  return;
12349
12379
  }
@@ -12351,8 +12381,8 @@ async function installCiEnforcement(root) {
12351
12381
  ui.success(`Created ${path40.relative(root, workflowPath)}`);
12352
12382
  }
12353
12383
  function renderCiEnforcementWorkflow() {
12354
- return `# haive:enforcement-workflow:start
12355
- name: haive-enforcement
12384
+ return `# hivelore:enforcement-workflow:start
12385
+ name: hivelore-enforcement
12356
12386
 
12357
12387
  on:
12358
12388
  pull_request:
@@ -12360,7 +12390,7 @@ on:
12360
12390
  branches: [main, master]
12361
12391
 
12362
12392
  jobs:
12363
- haive-enforcement:
12393
+ hivelore-enforcement:
12364
12394
  runs-on: ubuntu-latest
12365
12395
  permissions:
12366
12396
  contents: read
@@ -12377,11 +12407,11 @@ jobs:
12377
12407
  - name: Enforce Hivelore policy
12378
12408
  id: gate
12379
12409
  env:
12380
- HAIVE_BASE_SHA: \${{ github.event.pull_request.base.sha || github.event.before }}
12381
- HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
12410
+ HIVELORE_BASE_SHA: \${{ github.event.pull_request.base.sha || github.event.before }}
12411
+ HIVELORE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
12382
12412
  run: |
12383
12413
  set +e
12384
- hivelore enforce ci --json > "$RUNNER_TEMP/haive-gate.json"
12414
+ hivelore enforce ci --json > "$RUNNER_TEMP/hivelore-gate.json"
12385
12415
  echo "exit_code=$?" >> "$GITHUB_OUTPUT"
12386
12416
  exit 0
12387
12417
  - name: Upsert prevention receipt
@@ -12392,7 +12422,7 @@ jobs:
12392
12422
  run: |
12393
12423
  if [ -z "\${GH_TOKEN:-}" ] || ! command -v gh >/dev/null 2>&1; then exit 0; fi
12394
12424
  receipt="$(hivelore stats receipt --since 7d --json 2>/dev/null)" || exit 0
12395
- gate="$(cat "$RUNNER_TEMP/haive-gate.json" 2>/dev/null)" || gate='{"findings":[]}'
12425
+ gate="$(cat "$RUNNER_TEMP/hivelore-gate.json" 2>/dev/null)" || gate='{"findings":[]}'
12396
12426
  body="$(jq -nr --arg marker '<!-- haive:prevention-receipt -->' --argjson receipt "$receipt" --argjson gate "$gate" '
12397
12427
  $marker + "
12398
12428
  ## Hivelore prevention receipt
@@ -12421,7 +12451,7 @@ Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **
12421
12451
  - name: Fail when enforcement blocked
12422
12452
  if: steps.gate.outputs.exit_code != '0'
12423
12453
  run: exit \${{ steps.gate.outputs.exit_code }}
12424
- # haive:enforcement-workflow:end
12454
+ # hivelore:enforcement-workflow:end
12425
12455
  `;
12426
12456
  }
12427
12457
  var CONTENT_CATCH_CODES = /* @__PURE__ */ new Set(["sensor-block", "precommit-policy-block"]);
@@ -12632,9 +12662,43 @@ import { readFile as readFile19, writeFile as writeFile26 } from "fs/promises";
12632
12662
  import path41 from "path";
12633
12663
  import "commander";
12634
12664
  import { findProjectRoot as findProjectRoot38 } from "@hivelore/core";
12635
- import { execFile as execFile6 } from "child_process";
12665
+ import { execFile as execFile6, spawn as spawn5 } from "child_process";
12636
12666
  import { promisify as promisify6 } from "util";
12637
12667
  var exec4 = promisify6(execFile6);
12668
+ async function createAndPushTag(root, push) {
12669
+ const version = await readCurrentVersion(root);
12670
+ for (const rel of VERSION_FILES2.slice(1)) {
12671
+ const file = path41.join(root, rel);
12672
+ if (!existsSync43(file)) continue;
12673
+ const v = JSON.parse(await readFile19(file, "utf8")).version;
12674
+ if (v !== version) {
12675
+ ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
12676
+ process.exitCode = 1;
12677
+ return null;
12678
+ }
12679
+ }
12680
+ const dirty = (await exec4("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
12681
+ if (dirty.length > 0) {
12682
+ ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
12683
+ process.exitCode = 1;
12684
+ return null;
12685
+ }
12686
+ const tag = `v${version}`;
12687
+ const existing = (await exec4("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
12688
+ if (existing) {
12689
+ ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
12690
+ process.exitCode = 1;
12691
+ return null;
12692
+ }
12693
+ await exec4("git", ["tag", tag], { cwd: root });
12694
+ ui.success(`Created ${tag} at HEAD.`);
12695
+ if (push) {
12696
+ await exec4("git", ["push"], { cwd: root });
12697
+ await exec4("git", ["push", "origin", tag], { cwd: root });
12698
+ ui.success(`Pushed branch and ${tag}.`);
12699
+ }
12700
+ return tag;
12701
+ }
12638
12702
  var VERSION_FILES2 = [
12639
12703
  "package.json",
12640
12704
  "packages/core/package.json",
@@ -12700,38 +12764,45 @@ ${heading}
12700
12764
  });
12701
12765
  release.command("tag").description("Create vX.Y.Z at HEAD (from the lockstep version), push the branch and that one tag.").option("--no-push", "create the tag locally without pushing").option("-d, --dir <dir>", "project root").action(async (opts) => {
12702
12766
  const root = findProjectRoot38(opts.dir);
12703
- const version = await readCurrentVersion(root);
12704
- for (const rel of VERSION_FILES2.slice(1)) {
12705
- const file = path41.join(root, rel);
12706
- if (!existsSync43(file)) continue;
12707
- const v = JSON.parse(await readFile19(file, "utf8")).version;
12708
- if (v !== version) {
12709
- ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
12767
+ const tag = await createAndPushTag(root, opts.push !== false);
12768
+ if (tag && opts.push !== false) {
12769
+ ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
12770
+ }
12771
+ });
12772
+ release.command("ship").description("One-shot release close-out: git pull --rebase \u2192 tag + push \u2192 poll CI (enforce finish --wait). Run it after committing the bump.").option("--no-push", "tag locally without pushing (skips the CI wait)").option("-d, --dir <dir>", "project root").action(async (opts) => {
12773
+ const root = findProjectRoot38(opts.dir);
12774
+ const push = opts.push !== false;
12775
+ if (push) {
12776
+ try {
12777
+ await exec4("git", ["pull", "--rebase"], { cwd: root });
12778
+ ui.success("Rebased on origin.");
12779
+ } catch (err) {
12780
+ ui.error(`git pull --rebase failed \u2014 resolve it, then re-run \`hivelore release ship\`:
12781
+ ${err instanceof Error ? err.message : String(err)}`);
12710
12782
  process.exitCode = 1;
12711
12783
  return;
12712
12784
  }
12713
12785
  }
12714
- const dirty = (await exec4("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
12715
- if (dirty.length > 0) {
12716
- ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
12717
- process.exitCode = 1;
12786
+ const tag = await createAndPushTag(root, push);
12787
+ if (!tag) return;
12788
+ if (!push) {
12789
+ ui.info(`Created ${tag} locally (no push). Run \`hivelore release ship\` without --no-push to publish + poll CI.`);
12718
12790
  return;
12719
12791
  }
12720
- const tag = `v${version}`;
12721
- const existing = (await exec4("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
12722
- if (existing) {
12723
- ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
12724
- process.exitCode = 1;
12792
+ ui.info("Polling CI (enforce finish --wait)\u2026");
12793
+ const code = await new Promise((resolve) => {
12794
+ const child = spawn5(process.execPath, [process.argv[1], "enforce", "finish", "--wait", "--dir", root], {
12795
+ stdio: "inherit"
12796
+ });
12797
+ child.on("close", (c) => resolve(c ?? 1));
12798
+ child.on("error", () => resolve(1));
12799
+ });
12800
+ if (code !== 0) {
12801
+ ui.error("Shipped the tag, but the finish gate/CI did not pass \u2014 see the output above.");
12802
+ process.exitCode = code;
12725
12803
  return;
12726
12804
  }
12727
- await exec4("git", ["tag", tag], { cwd: root });
12728
- ui.success(`Created ${tag} at HEAD.`);
12729
- if (opts.push !== false) {
12730
- await exec4("git", ["push"], { cwd: root });
12731
- await exec4("git", ["push", "origin", tag], { cwd: root });
12732
- ui.success(`Pushed branch and ${tag}.`);
12733
- ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
12734
- }
12805
+ ui.success(`Shipped ${tag} \u2014 CI green. npm publication stays a human step (\`pnpm run publish:all\`).`);
12735
12806
  });
12736
12807
  }
12737
12808
 
@@ -12755,6 +12826,7 @@ import path42 from "path";
12755
12826
  import { promisify as promisify7 } from "util";
12756
12827
  import "commander";
12757
12828
  import {
12829
+ extractCorrectApproachExamples,
12758
12830
  extractSensorExamples,
12759
12831
  appendSensorEvaluations as appendSensorEvaluations2,
12760
12832
  assessSensorHealth as assessSensorHealth4,
@@ -12762,7 +12834,7 @@ import {
12762
12834
  findProjectRoot as findProjectRoot39,
12763
12835
  isRetiredMemory as isRetiredMemory3,
12764
12836
  judgeProposedSensor,
12765
- loadConfig as loadConfig13,
12837
+ loadConfig as loadConfig14,
12766
12838
  loadSensorLedger as loadSensorLedger4,
12767
12839
  loadMemoriesFromDir as loadMemoriesFromDir16,
12768
12840
  normalizeFramework,
@@ -12886,7 +12958,7 @@ function registerSensors(program2) {
12886
12958
  }
12887
12959
  }
12888
12960
  }
12889
- const config = await loadConfig13(paths);
12961
+ const config = await loadConfig14(paths);
12890
12962
  const runCommands = opts.commands || config.enforcement?.runCommandSensors === true;
12891
12963
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
12892
12964
  const allSensorMemories = await runnableSensorMemories(paths, false);
@@ -13049,7 +13121,8 @@ function registerSensors(program2) {
13049
13121
  const currentTargets = await readPresumedCorrectTargets(root, anchorPaths);
13050
13122
  const check = sensorSelfCheck2(sensor, {
13051
13123
  currentTargets,
13052
- badExamples: extractSensorExamples(found.memory.body)
13124
+ badExamples: extractSensorExamples(found.memory.body),
13125
+ correctExamples: extractCorrectApproachExamples(found.memory.body)
13053
13126
  });
13054
13127
  if (currentTargets.length > 0 && !check.silent_on_current) {
13055
13128
  ui.error(
@@ -13058,6 +13131,13 @@ function registerSensors(program2) {
13058
13131
  process.exitCode = 1;
13059
13132
  return;
13060
13133
  }
13134
+ if (check.fires_on_correct === true) {
13135
+ ui.error(
13136
+ "Refusing to block: this sensor fires on the lesson's OWN recommended fix (its `Instead, use:` approach) \u2014 it is inverted and would block the correct code, never the mistake. Point the pattern at the FAULTY usage, or pass --force."
13137
+ );
13138
+ process.exitCode = 1;
13139
+ return;
13140
+ }
13061
13141
  if (check.fires_on_bad === true) {
13062
13142
  ui.success("Self-check passed: fires on the lesson's bad example, silent on current code.");
13063
13143
  } else if (check.fires_on_bad === false) {
@@ -13106,7 +13186,7 @@ function registerSensors(program2) {
13106
13186
  }
13107
13187
  }
13108
13188
  const root2 = findProjectRoot39(opts.dir);
13109
- const { proposeSensor } = await import("./server-ZE5Y3Z4A.js");
13189
+ const { proposeSensor } = await import("./server-UAD33QQZ.js");
13110
13190
  const out = await proposeSensor(
13111
13191
  {
13112
13192
  memory_id: id,
@@ -13168,6 +13248,7 @@ function registerSensors(program2) {
13168
13248
  ...opts.badExample ? [opts.badExample] : [],
13169
13249
  ...extractSensorExamples(found.memory.body)
13170
13250
  ];
13251
+ const correctExamples = extractCorrectApproachExamples(found.memory.body);
13171
13252
  const sensor = {
13172
13253
  kind: "regex",
13173
13254
  pattern: opts.pattern,
@@ -13180,7 +13261,7 @@ function registerSensors(program2) {
13180
13261
  autogen: false,
13181
13262
  last_fired: null
13182
13263
  };
13183
- const verdict = judgeProposedSensor(sensor, { currentTargets, badExamples });
13264
+ const verdict = judgeProposedSensor(sensor, { currentTargets, badExamples, correctExamples });
13184
13265
  if (opts.json && !verdict.accepted) {
13185
13266
  console.log(JSON.stringify({
13186
13267
  accepted: verdict.accepted,
@@ -13197,6 +13278,8 @@ function registerSensors(program2) {
13197
13278
  ui.error(`Rejected (${verdict.reason}).`);
13198
13279
  if (verdict.reason === "fires-on-current") {
13199
13280
  ui.warn(`Fires on the CURRENT correct code in: ${verdict.self_check.fired_on.join(", ")}. Add/tighten --absent, then re-run.`);
13281
+ } else if (verdict.reason === "fires-on-correct") {
13282
+ ui.warn("Inverted: the pattern matches the lesson's OWN recommended fix (its `Instead, use:` approach) \u2014 it would block correct code, never the mistake. Point --pattern at the FAULTY usage, then re-run.");
13200
13283
  } else if (verdict.reason === "missed-bad-example") {
13201
13284
  ui.warn("Did not match the bad example \u2014 the pattern won't catch the mistake. Adjust --pattern, then re-run.");
13202
13285
  } else if (verdict.reason === "brittle") {
@@ -13721,7 +13804,7 @@ import "commander";
13721
13804
  import {
13722
13805
  buildDashboard,
13723
13806
  findProjectRoot as findProjectRoot41,
13724
- loadConfig as loadConfig14,
13807
+ loadConfig as loadConfig15,
13725
13808
  loadMemoriesFromDir as loadMemoriesFromDir18,
13726
13809
  loadPreventionEvents as loadPreventionEvents4,
13727
13810
  loadUsageIndex as loadUsageIndex16,
@@ -13741,7 +13824,7 @@ function registerDashboard(program2) {
13741
13824
  const memories = existsSync46(paths.memoriesDir) ? await loadMemoriesFromDir18(paths.memoriesDir) : [];
13742
13825
  const usage = await loadUsageIndex16(paths);
13743
13826
  const preventionEvents = await loadPreventionEvents4(paths);
13744
- const config = await loadConfig14(paths);
13827
+ const config = await loadConfig15(paths);
13745
13828
  const top = Math.max(1, Number.parseInt(opts.top ?? "10", 10) || 10);
13746
13829
  const dormantDays = opts.dormantDays ? Number.parseInt(opts.dormantDays, 10) : void 0;
13747
13830
  const report = buildDashboard(memories, usage, {
@@ -13872,7 +13955,7 @@ import { findProjectRoot as findProjectRoot42 } from "@hivelore/core";
13872
13955
  var exec6 = promisify8(execFile8);
13873
13956
  function registerDevLink(program2) {
13874
13957
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
13875
- dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore (or legacy @hiveai) install so the global binary runs your local code.").option("-d, --dir <dir>", "repo root (default: discovered from cwd)").option("--json", "emit a machine-readable summary", false).action(async (opts) => {
13958
+ dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore install so the global binary runs your local code.").option("-d, --dir <dir>", "repo root (default: discovered from cwd)").option("--json", "emit a machine-readable summary", false).action(async (opts) => {
13876
13959
  const root = findProjectRoot42(opts.dir);
13877
13960
  if (!existsSync47(path44.join(root, "packages", "cli", "dist", "index.js"))) {
13878
13961
  ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
@@ -13885,9 +13968,9 @@ function registerDevLink(program2) {
13885
13968
  } catch {
13886
13969
  globalModules = path44.join(path44.dirname(path44.dirname(process.execPath)), "lib", "node_modules");
13887
13970
  }
13888
- const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path44.join(globalModules, scope)).filter((dir) => existsSync47(dir));
13971
+ const scopeDirs = ["@hivelore"].map((scope) => path44.join(globalModules, scope)).filter((dir) => existsSync47(dir));
13889
13972
  if (scopeDirs.length === 0) {
13890
- ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
13973
+ ui.error(`No global @hivelore install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
13891
13974
  process.exitCode = 1;
13892
13975
  return;
13893
13976
  }
@@ -14173,7 +14256,7 @@ function registerBridges(program2) {
14173
14256
 
14174
14257
  // src/index.ts
14175
14258
  var program = new Command48();
14176
- program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.49.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
14259
+ program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.52.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
14177
14260
  registerInit(program);
14178
14261
  registerResolveProject(program);
14179
14262
  registerEnforce(program);