@hivelore/cli 0.47.0 → 0.51.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-HBS7PNNY.js";
16
+ } from "./chunk-UK4CWVS7.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.47.0"}`;
3765
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.51.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) {
@@ -4170,7 +4182,11 @@ function registerInit(program2) {
4170
4182
  "!.ai/.cache/.gitignore",
4171
4183
  ".ai/.runtime/*",
4172
4184
  "!.ai/.runtime/.gitignore",
4173
- "!.ai/.runtime/README.md"
4185
+ "!.ai/.runtime/README.md",
4186
+ // Machine-local tool-usage telemetry — like .cache/.runtime, it changes on every invocation
4187
+ // and is never team truth (eval baselines exclude local usage counters). Committing it made the
4188
+ // working tree perpetually dirty and forced a stash before every release.
4189
+ ".ai/.usage/"
4174
4190
  ]);
4175
4191
  ui.success(`Hivelore initialized at ${root}${autopilot ? " (autopilot mode)" : ""}`);
4176
4192
  if (json) {
@@ -4729,7 +4745,7 @@ import {
4729
4745
  isDecaying,
4730
4746
  isStackPackSeed,
4731
4747
  loadCodeMap as loadCodeMap5,
4732
- loadConfig as loadConfig3,
4748
+ loadConfig as loadConfig4,
4733
4749
  loadMemoriesFromDir as loadMemoriesFromDir7,
4734
4750
  loadSensorLedger,
4735
4751
  loadUsageIndex as loadUsageIndex3,
@@ -4767,7 +4783,7 @@ function registerSync(program2) {
4767
4783
  const log = (msg) => {
4768
4784
  if (!opts.quiet) console.log(msg);
4769
4785
  };
4770
- const config = await loadConfig3(paths);
4786
+ const config = await loadConfig4(paths);
4771
4787
  const autoApproveDelayHours = config.autoApproveDelayHours ?? null;
4772
4788
  const autoPromoteMinReads = config.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads;
4773
4789
  const autoRepair = config.autoRepair ?? {};
@@ -5433,7 +5449,7 @@ import {
5433
5449
  suggestSensorSeed,
5434
5450
  findProjectRoot as findProjectRoot10,
5435
5451
  inferModulesFromPaths as inferModulesFromPaths2,
5436
- loadConfig as loadConfig4,
5452
+ loadConfig as loadConfig5,
5437
5453
  loadMemoriesFromDir as loadMemoriesFromDir8,
5438
5454
  memoryFilePath as memoryFilePath3,
5439
5455
  resolveHaivePaths as resolveHaivePaths9,
@@ -5473,7 +5489,7 @@ function registerMemoryAdd(memory2) {
5473
5489
  process.exitCode = 1;
5474
5490
  return;
5475
5491
  }
5476
- const config = await loadConfig4(paths);
5492
+ const config = await loadConfig5(paths);
5477
5493
  const userTags = parseCsv2(opts.tags);
5478
5494
  const anchorPaths = parseCsv2(opts.paths ?? opts.files);
5479
5495
  const activation = opts.type === "skill" && (opts.activationKeyword || opts.activationGlob || opts.activationAlways) ? {
@@ -6125,7 +6141,7 @@ import path22 from "path";
6125
6141
  import "commander";
6126
6142
  import {
6127
6143
  findProjectRoot as findProjectRoot16,
6128
- loadConfig as loadConfig5,
6144
+ loadConfig as loadConfig6,
6129
6145
  resolveHaivePaths as resolveHaivePaths15
6130
6146
  } from "@hivelore/core";
6131
6147
  async function readDependencyMap(root) {
@@ -6206,7 +6222,7 @@ function registerMemorySeed(memory2) {
6206
6222
  ui.success(`Seeded ${total} starter memor${total === 1 ? "y" : "ies"}: ${seededStacks.join(", ")}`);
6207
6223
  ui.info("Kept at background priority. Anchor them to a real file (or replace them) to make them high-signal:");
6208
6224
  ui.info(" hivelore memory update <id> --paths <key-file> # anchor a seed to a file");
6209
- const config = await loadConfig5(paths);
6225
+ const config = await loadConfig6(paths);
6210
6226
  if (config.autopilot || config.autoRepair?.corpus === true) {
6211
6227
  const repairs = await applyAutopilotRepairs(root, paths, {
6212
6228
  applyConfig: false,
@@ -6675,7 +6691,7 @@ import {
6675
6691
  } from "@hivelore/core";
6676
6692
  function registerMemoryVerify(memory2) {
6677
6693
  memory2.command("verify").description(
6678
- "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"
6679
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) => {
6680
6696
  const root = findProjectRoot24(opts.dir);
6681
6697
  const paths = resolveHaivePaths23(root);
@@ -6962,7 +6978,7 @@ function registerMemoryDigest(program2) {
6962
6978
  }
6963
6979
 
6964
6980
  // src/commands/session-end.ts
6965
- 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";
6966
6982
  import { existsSync as existsSync32 } from "fs";
6967
6983
  import { spawn as spawn2 } from "child_process";
6968
6984
  import path29 from "path";
@@ -6971,7 +6987,7 @@ import {
6971
6987
  distillFailureObservations,
6972
6988
  buildFrontmatter as buildFrontmatter5,
6973
6989
  findProjectRoot as findProjectRoot27,
6974
- loadConfig as loadConfig6,
6990
+ loadConfig as loadConfig7,
6975
6991
  loadMemoriesFromDir as loadMemoriesFromDir10,
6976
6992
  loadPreventionEvents,
6977
6993
  readUsageEvents,
@@ -7324,10 +7340,10 @@ ${note}` : note;
7324
7340
  const cleanupObservations = async () => {
7325
7341
  if (!opts.auto) return;
7326
7342
  const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
7327
- if (existsSync32(obsFile)) await rm(obsFile).catch(() => {
7343
+ if (existsSync32(obsFile)) await rm2(obsFile).catch(() => {
7328
7344
  });
7329
7345
  };
7330
- const config = await loadConfig6(paths);
7346
+ const config = await loadConfig7(paths);
7331
7347
  if (opts.auto && config.autoSessionRecap === false) {
7332
7348
  if (config.sessionHandoff) {
7333
7349
  const diffStat = await runGit(root, ["diff", "--stat", "HEAD"]).catch(() => "");
@@ -7954,7 +7970,7 @@ import {
7954
7970
  computeGatePrecision,
7955
7971
  computeEvalTrend,
7956
7972
  findProjectRoot as findProjectRoot31,
7957
- loadConfig as loadConfig7,
7973
+ loadConfig as loadConfig8,
7958
7974
  loadEvalHistory,
7959
7975
  loadPreventionEvents as loadPreventionEvents3,
7960
7976
  loadUsageIndex as loadUsageIndex13,
@@ -8061,7 +8077,7 @@ function registerEval(program2) {
8061
8077
  const [usage, preventionEvents, config] = await Promise.all([
8062
8078
  loadUsageIndex13(paths),
8063
8079
  loadPreventionEvents3(paths),
8064
- loadConfig7(paths)
8080
+ loadConfig8(paths)
8065
8081
  ]);
8066
8082
  const gatePrecision = computeGatePrecision(
8067
8083
  preventionEvents,
@@ -8389,7 +8405,7 @@ import {
8389
8405
  aggregateUsage as aggregateUsage2,
8390
8406
  buildFrontmatter as buildFrontmatter6,
8391
8407
  findProjectRoot as findProjectRoot32,
8392
- loadConfig as loadConfig8,
8408
+ loadConfig as loadConfig9,
8393
8409
  loadMemoriesFromDir as loadMemoriesFromDir12,
8394
8410
  memoryFilePath as memoryFilePath6,
8395
8411
  parseSince as parseSince2,
@@ -8461,7 +8477,7 @@ function registerMemorySuggest(memory2) {
8461
8477
  inferred_type: inferType(v.tools, query)
8462
8478
  })).sort((a, b) => b.count - a.count);
8463
8479
  if (opts.autoSave) {
8464
- const config = await loadConfig8(paths);
8480
+ const config = await loadConfig9(paths);
8465
8481
  const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
8466
8482
  const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
8467
8483
  const status = config.defaultStatus === "validated" ? "validated" : "draft";
@@ -8680,7 +8696,7 @@ import {
8680
8696
  findProjectRoot as findProjectRoot34,
8681
8697
  getUsage as getUsage8,
8682
8698
  retirementSignal as retirementSignal2,
8683
- loadConfig as loadConfig9,
8699
+ loadConfig as loadConfig10,
8684
8700
  loadMemoriesFromDir as loadMemoriesFromDir13,
8685
8701
  loadUsageIndex as loadUsageIndex14,
8686
8702
  resolveHaivePaths as resolveHaivePaths32,
@@ -8698,7 +8714,7 @@ function registerMemoryArchive(memory2) {
8698
8714
  process.exitCode = 1;
8699
8715
  return;
8700
8716
  }
8701
- const config = await loadConfig9(paths);
8717
+ const config = await loadConfig10(paths);
8702
8718
  const defaultWindow = config.enforcement?.decayAfterDays ? `${config.enforcement.decayAfterDays}d` : "180d";
8703
8719
  const minDays = parseDays(opts.since ?? defaultWindow);
8704
8720
  if (minDays === null) {
@@ -8812,7 +8828,7 @@ import {
8812
8828
  getUsage as getUsage9,
8813
8829
  isStackPackSeed as isStackPackSeed2,
8814
8830
  loadCodeMap as loadCodeMap7,
8815
- loadConfig as loadConfig10,
8831
+ loadConfig as loadConfig11,
8816
8832
  loadMemoriesFromDirDetailed,
8817
8833
  loadSensorLedger as loadSensorLedger2,
8818
8834
  loadUsageIndex as loadUsageIndex15,
@@ -8902,12 +8918,12 @@ var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
8902
8918
  function registerDoctor(program2) {
8903
8919
  program2.command("doctor").description(
8904
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."
8905
- ).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) => {
8906
8922
  const root = findProjectRoot35(opts.dir);
8907
8923
  const paths = resolveHaivePaths33(root);
8908
8924
  const findings = [];
8909
8925
  const repairs = [];
8910
- const config = await loadConfig10(paths);
8926
+ const config = await loadConfig11(paths);
8911
8927
  if (!existsSync40(paths.haiveDir)) {
8912
8928
  if (opts.json) {
8913
8929
  console.log(JSON.stringify({
@@ -9142,7 +9158,7 @@ function registerDoctor(program2) {
9142
9158
  }
9143
9159
  const astSensorCount = sensorMemories.filter((m) => m.memory.frontmatter.sensor?.kind === "ast").length;
9144
9160
  if (astSensorCount > 0) {
9145
- const { astEngineAvailable: astEngineAvailable2 } = await import("./server-LNSXYVVW.js");
9161
+ const { astEngineAvailable: astEngineAvailable2 } = await import("./server-Z4M6ERWN.js");
9146
9162
  if (!await astEngineAvailable2()) {
9147
9163
  findings.push({
9148
9164
  severity: "warn",
@@ -9310,11 +9326,11 @@ function registerDoctor(program2) {
9310
9326
  severity: "info",
9311
9327
  code: "no-autopilot",
9312
9328
  message: "Autopilot is OFF \u2014 session recaps are not auto-saved on shutdown.",
9313
- 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)."
9314
9330
  });
9315
9331
  }
9316
- findings.push(...await collectInstallFindings(root, "0.47.0"));
9317
- findings.push(...await collectMcpRuntimeFindings(paths, "0.47.0"));
9332
+ findings.push(...await collectInstallFindings(root, "0.51.0"));
9333
+ findings.push(...await collectMcpRuntimeFindings(paths, "0.51.0"));
9318
9334
  findings.push(...await collectToolchainFindings(root));
9319
9335
  try {
9320
9336
  const legacyRaw = execSync("haive-mcp --version", {
@@ -9322,7 +9338,7 @@ function registerDoctor(program2) {
9322
9338
  timeout: 3e3,
9323
9339
  stdio: ["ignore", "pipe", "ignore"]
9324
9340
  }).trim();
9325
- const cliVersion = "0.47.0";
9341
+ const cliVersion = "0.51.0";
9326
9342
  if (legacyRaw && legacyRaw !== cliVersion) {
9327
9343
  findings.push({
9328
9344
  severity: "warn",
@@ -9449,12 +9465,16 @@ function emit(findings, opts, repairs = []) {
9449
9465
  "Next actions"
9450
9466
  ];
9451
9467
  const severityOrder = ["error", "warn", "info"];
9468
+ const isVisible = (f) => f.severity !== "info" || opts.all === true || f.alwaysShow === true;
9469
+ let hiddenInfo = 0;
9452
9470
  for (const section2 of sectionOrder) {
9453
9471
  const sectionFindings = classified.filter((f) => f.section === section2);
9454
- if (sectionFindings.length === 0) continue;
9472
+ const visible = sectionFindings.filter(isVisible);
9473
+ hiddenInfo += sectionFindings.length - visible.length;
9474
+ if (visible.length === 0) continue;
9455
9475
  console.log(ui.bold(section2));
9456
9476
  for (const sev of severityOrder) {
9457
- for (const f of sectionFindings.filter((x) => x.severity === sev)) {
9477
+ for (const f of visible.filter((x) => x.severity === sev)) {
9458
9478
  const icon = sev === "error" ? ui.red("\u2717") : sev === "warn" ? ui.yellow("\u26A0") : ui.dim("\u2139");
9459
9479
  console.log(`${icon} ${ui.bold(f.code)} ${f.message}`);
9460
9480
  if (opts.fix && f.fix) {
@@ -9466,6 +9486,9 @@ function emit(findings, opts, repairs = []) {
9466
9486
  }
9467
9487
  console.log();
9468
9488
  }
9489
+ if (hiddenInfo > 0) {
9490
+ ui.info(`${hiddenInfo} informational finding(s) hidden \u2014 run \`hivelore doctor --all\` to show them.`);
9491
+ }
9469
9492
  if (repairs.length > 0) {
9470
9493
  console.log(ui.bold("Autopilot repairs applied"));
9471
9494
  for (const repair of repairs) console.log(` ${ui.dim("\u2713")} ${repair.message}`);
@@ -9572,6 +9595,7 @@ async function collectHarnessCoverageFindings(codeMap, memories) {
9572
9595
  findings.push({
9573
9596
  severity: "info",
9574
9597
  code: "harness-coverage",
9598
+ alwaysShow: true,
9575
9599
  coverage_percent: pct2,
9576
9600
  message: `${covered}/${total} code-map files have validated memory anchors (${pct2}%). ` + coverageDesc + uncoveredHint,
9577
9601
  fix: pct2 < 50 && total > 10 ? `hivelore memory save --type gotcha|convention|architecture --paths <key-file> --scope team` : void 0,
@@ -9599,6 +9623,7 @@ function collectBehaviourCoverageFindings(codeMap, memories) {
9599
9623
  return [{
9600
9624
  severity: "info",
9601
9625
  code: "behaviour-coverage",
9626
+ alwaysShow: true,
9602
9627
  section: "Protection",
9603
9628
  coverage_percent: Math.round(cov.areasWithOracle.length / cov.mainAreas.length * 100),
9604
9629
  message: `Behaviour harness: ${renderBehaviourCoverageLine2(cov)}.` + uncoveredHint,
@@ -9951,7 +9976,7 @@ import "commander";
9951
9976
  import {
9952
9977
  antiPatternGateParams,
9953
9978
  findProjectRoot as findProjectRoot36,
9954
- loadConfig as loadConfig11,
9979
+ loadConfig as loadConfig12,
9955
9980
  resolveHaivePaths as resolveHaivePaths34
9956
9981
  } from "@hivelore/core";
9957
9982
  function registerPrecommit(program2) {
@@ -9967,7 +9992,7 @@ function registerPrecommit(program2) {
9967
9992
  const root = findProjectRoot36(opts.dir);
9968
9993
  const paths = resolveHaivePaths34(root);
9969
9994
  const ctx = { paths };
9970
- const config = await loadConfig11(paths);
9995
+ const config = await loadConfig12(paths);
9971
9996
  const gate = config.enforcement?.antiPatternGate ?? "anchored";
9972
9997
  const gateParams = antiPatternGateParams(gate);
9973
9998
  const blockOn = opts.blockOn ?? gateParams.block_on;
@@ -10116,7 +10141,7 @@ function registerResolveProject(program2) {
10116
10141
  // src/commands/enforce.ts
10117
10142
  import { execFile as execFile5, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
10118
10143
  import { existsSync as existsSync42, statSync as statSync4 } from "fs";
10119
- 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";
10120
10145
  import path40 from "path";
10121
10146
  import { promisify as promisify5 } from "util";
10122
10147
  import "commander";
@@ -10137,7 +10162,7 @@ import {
10137
10162
  hasRecentBriefingMarker,
10138
10163
  isFreshIsoDate,
10139
10164
  isRetiredMemory as isRetiredMemory2,
10140
- loadConfig as loadConfig12,
10165
+ loadConfig as loadConfig13,
10141
10166
  detectAgentContext,
10142
10167
  loadMemoriesFromDir as loadMemoriesFromDir15,
10143
10168
  loadSensorLedger as loadSensorLedger3,
@@ -10435,7 +10460,7 @@ function registerEnforce(program2) {
10435
10460
  const root = findProjectRoot37(opts.dir);
10436
10461
  const paths = resolveHaivePaths35(root);
10437
10462
  await mkdir16(paths.haiveDir, { recursive: true });
10438
- const current = await loadConfig12(paths);
10463
+ const current = await loadConfig13(paths);
10439
10464
  await saveConfig3(paths, {
10440
10465
  ...current,
10441
10466
  enforcement: {
@@ -10452,7 +10477,7 @@ function registerEnforce(program2) {
10452
10477
  policyPacks: ["architecture", "gotchas", "security", "domain", "release"]
10453
10478
  }
10454
10479
  });
10455
- ui.success("Hivelore strict enforcement enabled in .ai/haive.config.json");
10480
+ ui.success("Hivelore strict enforcement enabled in .ai/hivelore.config.json");
10456
10481
  if (opts.git !== false) await installGitEnforcement(root);
10457
10482
  if (opts.ci !== false) await installCiEnforcement(root);
10458
10483
  if (opts.claude !== false) {
@@ -10607,7 +10632,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
10607
10632
  const paths = resolveHaivePaths35(root);
10608
10633
  if (!existsSync42(paths.haiveDir)) return;
10609
10634
  if (!isWriteLikeTool(payload)) return;
10610
- const config = await loadConfig12(paths);
10635
+ const config = await loadConfig13(paths);
10611
10636
  if (config.enforcement?.requireBriefingFirst === false) return;
10612
10637
  const gate = config.enforcement?.preEditGate ?? "advise";
10613
10638
  const targetFiles = extractToolPaths(payload, root);
@@ -10621,7 +10646,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
10621
10646
  const contextText = buildPreEditContext(payload.tool_name ?? "write tool", targetFiles, missing, hasMarker);
10622
10647
  if (gate === "block") {
10623
10648
  console.error(
10624
- 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.'
10625
10650
  );
10626
10651
  process.exit(2);
10627
10652
  }
@@ -10687,7 +10712,7 @@ async function buildFinishReport(dir) {
10687
10712
  const root = findProjectRoot37(dir);
10688
10713
  const paths = resolveHaivePaths35(root);
10689
10714
  const initialized = existsSync42(paths.haiveDir);
10690
- const config = initialized ? await loadConfig12(paths) : {};
10715
+ const config = initialized ? await loadConfig13(paths) : {};
10691
10716
  const mode = config.enforcement?.mode ?? "strict";
10692
10717
  const findings = [];
10693
10718
  if (!initialized) {
@@ -10708,7 +10733,7 @@ async function buildFinishReport(dir) {
10708
10733
  }
10709
10734
  findings.push(...await checkFailureCapture(paths, config));
10710
10735
  findings.push(...await checkPostIncidentScaffolds(paths));
10711
- findings.push(...await checkBootstrapComplete(paths, config, true));
10736
+ findings.push(...await checkBootstrapComplete(paths, config, true, "pre-push"));
10712
10737
  const status = await getGitSyncStatus(root);
10713
10738
  if (!status.available) {
10714
10739
  findings.push({
@@ -10966,7 +10991,7 @@ async function runWithEnforcement(command, args, opts) {
10966
10991
  HAIVE_ENFORCEMENT: "strict",
10967
10992
  HAIVE_AGENT: "1",
10968
10993
  // wrapped process is an agent — process gates bind it (detectAgentContext)
10969
- HAIVE_TOOL_PROFILE: process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
10994
+ HIVELORE_TOOL_PROFILE: process.env.HIVELORE_TOOL_PROFILE ?? process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
10970
10995
  }
10971
10996
  });
10972
10997
  await new Promise((resolve, reject) => {
@@ -11054,7 +11079,7 @@ function looksLikeProductionCode(file) {
11054
11079
  if (/(^|\/)(test|tests|__tests__|__mocks__|e2e|fixtures)(\/|$)/.test(f)) return false;
11055
11080
  return PRODUCTION_CODE_EXT.test(f);
11056
11081
  }
11057
- async function checkBootstrapComplete(paths, config, productionCodeChanged) {
11082
+ async function checkBootstrapComplete(paths, config, productionCodeChanged, stage) {
11058
11083
  const gate = config.enforcement?.bootstrapGate ?? "block";
11059
11084
  if (gate === "off") return [];
11060
11085
  let projectContextRaw = "";
@@ -11080,7 +11105,8 @@ async function checkBootstrapComplete(paths, config, productionCodeChanged) {
11080
11105
  }];
11081
11106
  }
11082
11107
  const hasCodeAreas = assessment.metrics.mainAreas > 0;
11083
- const blocking = gate === "block" && hasCodeAreas && productionCodeChanged;
11108
+ const enforcedStage = stage === "pre-push" || stage === "ci";
11109
+ const blocking = gate === "block" && hasCodeAreas && productionCodeChanged && enforcedStage;
11084
11110
  const severity = blocking ? "error" : hasCodeAreas ? "warn" : "info";
11085
11111
  return [{
11086
11112
  severity,
@@ -11095,7 +11121,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11095
11121
  const root = findProjectRoot37(dir);
11096
11122
  const paths = resolveHaivePaths35(root);
11097
11123
  const initialized = existsSync42(paths.haiveDir);
11098
- const config = initialized ? await loadConfig12(paths) : {};
11124
+ const config = initialized ? await loadConfig13(paths) : {};
11099
11125
  if (initialized) {
11100
11126
  await applyLightweightRepairs(root, paths);
11101
11127
  if (stage === "pre-commit") await stageResyncedArtifacts(root, paths);
@@ -11128,7 +11154,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11128
11154
  findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
11129
11155
  });
11130
11156
  }
11131
- findings.push(...await inspectIntegrationVersions(root, "0.47.0"));
11157
+ findings.push(...await inspectIntegrationVersions(root, "0.51.0"));
11132
11158
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
11133
11159
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
11134
11160
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
@@ -11176,7 +11202,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11176
11202
  }
11177
11203
  {
11178
11204
  const changed = await getChangedFiles(root, stage).catch(() => []);
11179
- findings.push(...await checkBootstrapComplete(paths, config, changed.some(looksLikeProductionCode)));
11205
+ findings.push(...await checkBootstrapComplete(paths, config, changed.some(looksLikeProductionCode), stage));
11180
11206
  }
11181
11207
  const agentContext = detectAgentContext();
11182
11208
  const relaxForHuman = stage !== "ci" && !agentContext.agent && (config.enforcement?.humanCommits ?? "relaxed") === "relaxed";
@@ -11339,7 +11365,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
11339
11365
  }];
11340
11366
  }
11341
11367
  if (stage === "pre-commit" || stage === "pre-push") {
11342
- const cfg = await loadConfig12(paths).catch(() => ({}));
11368
+ const cfg = await loadConfig13(paths).catch(() => ({}));
11343
11369
  if (cfg.enforcement?.autoBrief !== false) {
11344
11370
  await writeBriefingMarker2(paths, {
11345
11371
  sessionId,
@@ -11412,9 +11438,8 @@ async function runPrecommitPolicy(paths, gate, stage, config) {
11412
11438
  semantic: true
11413
11439
  }, { paths });
11414
11440
  const sensorFindings = await runSensorGate(paths, snapshot.diff, stage);
11415
- const reviewWarnings = result.warnings.filter(
11416
- (w) => w.level === "review" && !w.reasons.includes("sensor")
11417
- );
11441
+ const showReview = config.enforcement?.reviewMatches === true || gate === "review";
11442
+ const reviewWarnings = showReview ? result.warnings.filter((w) => w.level === "review" && !w.reasons.includes("sensor")) : [];
11418
11443
  const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
11419
11444
  const reviewSeenFile = path40.join(paths.runtimeDir, "enforcement", "review-seen.json");
11420
11445
  let reviewSeen = {};
@@ -11635,7 +11660,7 @@ async function runSensorGate(paths, diff, stage) {
11635
11660
  }
11636
11661
  }
11637
11662
  }
11638
- const config = await loadConfig12(paths).catch(() => ({}));
11663
+ const config = await loadConfig13(paths).catch(() => ({}));
11639
11664
  if (config?.enforcement?.runCommandSensors === true) {
11640
11665
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
11641
11666
  const specs = selectCommandSensors(scannable, changedPaths).filter((sp) => !seen.has(sp.memory_id));
@@ -11720,12 +11745,13 @@ command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlo
11720
11745
  }
11721
11746
  return findings;
11722
11747
  } catch (err) {
11748
+ const inCi = stage === "ci";
11723
11749
  return [{
11724
- severity: "warn",
11750
+ severity: inCi ? "error" : "warn",
11725
11751
  code: "sensor-gate-errored",
11726
- message: `The sensor gate itself errored, so NO sensors were evaluated on this diff: ` + `${err instanceof Error ? err.message : String(err)}`.slice(0, 400),
11727
- fix: "Run `hivelore sensors check` to reproduce, and `hivelore doctor` for setup drift. The lessons' protection is OFF until this is fixed.",
11728
- 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
11729
11755
  }];
11730
11756
  }
11731
11757
  }
@@ -11762,7 +11788,7 @@ async function cleanupRuntimeDir(runtimeDir) {
11762
11788
  removed += await cleanupEnforcementDir(path40.join(runtimeDir, entry.name));
11763
11789
  continue;
11764
11790
  }
11765
- await rm2(path40.join(runtimeDir, entry.name), { recursive: true, force: true });
11791
+ await rm3(path40.join(runtimeDir, entry.name), { recursive: true, force: true });
11766
11792
  removed++;
11767
11793
  }
11768
11794
  await writeFile25(path40.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
@@ -11781,7 +11807,7 @@ async function cleanupCacheDir(cacheDir) {
11781
11807
  const entries = await readdir4(cacheDir, { withFileTypes: true }).catch(() => []);
11782
11808
  for (const entry of entries) {
11783
11809
  if (entry.name === ".gitignore") continue;
11784
- await rm2(path40.join(cacheDir, entry.name), { recursive: true, force: true });
11810
+ await rm3(path40.join(cacheDir, entry.name), { recursive: true, force: true });
11785
11811
  removed++;
11786
11812
  }
11787
11813
  await writeFile25(path40.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
@@ -11792,7 +11818,7 @@ async function cleanupEnforcementDir(enforcementDir) {
11792
11818
  const entries = await readdir4(enforcementDir, { withFileTypes: true }).catch(() => []);
11793
11819
  for (const entry of entries) {
11794
11820
  if (entry.name === "briefings") continue;
11795
- await rm2(path40.join(enforcementDir, entry.name), { recursive: true, force: true });
11821
+ await rm3(path40.join(enforcementDir, entry.name), { recursive: true, force: true });
11796
11822
  removed++;
11797
11823
  }
11798
11824
  return removed;
@@ -11909,8 +11935,8 @@ async function getPolicyDiffSnapshot(root, stage) {
11909
11935
  return { diff: "", paths: [], source: "none" };
11910
11936
  }
11911
11937
  async function resolveCiDiffRange(root) {
11912
- const explicitBase = cleanGitSha(process.env.HAIVE_BASE_SHA ?? process.env.HAIVE_BASE_REF);
11913
- 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";
11914
11940
  if (explicitBase && await gitCommitExists(root, explicitBase)) {
11915
11941
  return `${explicitBase}...${explicitHead}`;
11916
11942
  }
@@ -12258,7 +12284,6 @@ async function installGitEnforcement(root) {
12258
12284
  await mkdir16(hooksDir, { recursive: true });
12259
12285
  const resolveCli = `_hivelore() {
12260
12286
  if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
12261
- elif command -v haive >/dev/null 2>&1; then haive "$@"
12262
12287
  else return 0
12263
12288
  fi
12264
12289
  }`;
@@ -12325,20 +12350,30 @@ ${hook.body}`, "utf8");
12325
12350
  ui.success("Installed git hooks: pre-commit, pre-push, commit-msg (blocking) + post-merge, post-rewrite (sync)");
12326
12351
  }
12327
12352
  async function installCiEnforcement(root) {
12328
- const workflowPath = path40.join(root, ".github", "workflows", "haive-enforcement.yml");
12329
- 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 });
12330
12357
  const workflow = renderCiEnforcementWorkflow();
12331
- if (existsSync42(workflowPath)) {
12332
- const existing = await readFile18(workflowPath, "utf8");
12333
- const start = "# haive:enforcement-workflow:start";
12334
- const end = "# haive:enforcement-workflow:end";
12335
- const startAt = existing.indexOf(start);
12336
- 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);
12337
12364
  if (startAt >= 0 && endAt > startAt) {
12338
- 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");
12339
12366
  ui.success(`Updated ${path40.relative(root, workflowPath)} managed block`);
12340
- } else {
12367
+ } else if (source === workflowPath) {
12341
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)}.`);
12342
12377
  }
12343
12378
  return;
12344
12379
  }
@@ -12346,8 +12381,8 @@ async function installCiEnforcement(root) {
12346
12381
  ui.success(`Created ${path40.relative(root, workflowPath)}`);
12347
12382
  }
12348
12383
  function renderCiEnforcementWorkflow() {
12349
- return `# haive:enforcement-workflow:start
12350
- name: haive-enforcement
12384
+ return `# hivelore:enforcement-workflow:start
12385
+ name: hivelore-enforcement
12351
12386
 
12352
12387
  on:
12353
12388
  pull_request:
@@ -12355,7 +12390,7 @@ on:
12355
12390
  branches: [main, master]
12356
12391
 
12357
12392
  jobs:
12358
- haive-enforcement:
12393
+ hivelore-enforcement:
12359
12394
  runs-on: ubuntu-latest
12360
12395
  permissions:
12361
12396
  contents: read
@@ -12372,11 +12407,11 @@ jobs:
12372
12407
  - name: Enforce Hivelore policy
12373
12408
  id: gate
12374
12409
  env:
12375
- HAIVE_BASE_SHA: \${{ github.event.pull_request.base.sha || github.event.before }}
12376
- 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 }}
12377
12412
  run: |
12378
12413
  set +e
12379
- hivelore enforce ci --json > "$RUNNER_TEMP/haive-gate.json"
12414
+ hivelore enforce ci --json > "$RUNNER_TEMP/hivelore-gate.json"
12380
12415
  echo "exit_code=$?" >> "$GITHUB_OUTPUT"
12381
12416
  exit 0
12382
12417
  - name: Upsert prevention receipt
@@ -12387,7 +12422,7 @@ jobs:
12387
12422
  run: |
12388
12423
  if [ -z "\${GH_TOKEN:-}" ] || ! command -v gh >/dev/null 2>&1; then exit 0; fi
12389
12424
  receipt="$(hivelore stats receipt --since 7d --json 2>/dev/null)" || exit 0
12390
- 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":[]}'
12391
12426
  body="$(jq -nr --arg marker '<!-- haive:prevention-receipt -->' --argjson receipt "$receipt" --argjson gate "$gate" '
12392
12427
  $marker + "
12393
12428
  ## Hivelore prevention receipt
@@ -12416,7 +12451,7 @@ Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **
12416
12451
  - name: Fail when enforcement blocked
12417
12452
  if: steps.gate.outputs.exit_code != '0'
12418
12453
  run: exit \${{ steps.gate.outputs.exit_code }}
12419
- # haive:enforcement-workflow:end
12454
+ # hivelore:enforcement-workflow:end
12420
12455
  `;
12421
12456
  }
12422
12457
  var CONTENT_CATCH_CODES = /* @__PURE__ */ new Set(["sensor-block", "precommit-policy-block"]);
@@ -12627,9 +12662,43 @@ import { readFile as readFile19, writeFile as writeFile26 } from "fs/promises";
12627
12662
  import path41 from "path";
12628
12663
  import "commander";
12629
12664
  import { findProjectRoot as findProjectRoot38 } from "@hivelore/core";
12630
- import { execFile as execFile6 } from "child_process";
12665
+ import { execFile as execFile6, spawn as spawn5 } from "child_process";
12631
12666
  import { promisify as promisify6 } from "util";
12632
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
+ }
12633
12702
  var VERSION_FILES2 = [
12634
12703
  "package.json",
12635
12704
  "packages/core/package.json",
@@ -12695,38 +12764,45 @@ ${heading}
12695
12764
  });
12696
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) => {
12697
12766
  const root = findProjectRoot38(opts.dir);
12698
- const version = await readCurrentVersion(root);
12699
- for (const rel of VERSION_FILES2.slice(1)) {
12700
- const file = path41.join(root, rel);
12701
- if (!existsSync43(file)) continue;
12702
- const v = JSON.parse(await readFile19(file, "utf8")).version;
12703
- if (v !== version) {
12704
- 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)}`);
12705
12782
  process.exitCode = 1;
12706
12783
  return;
12707
12784
  }
12708
12785
  }
12709
- const dirty = (await exec4("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
12710
- if (dirty.length > 0) {
12711
- ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
12712
- 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.`);
12713
12790
  return;
12714
12791
  }
12715
- const tag = `v${version}`;
12716
- const existing = (await exec4("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
12717
- if (existing) {
12718
- ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
12719
- 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;
12720
12803
  return;
12721
12804
  }
12722
- await exec4("git", ["tag", tag], { cwd: root });
12723
- ui.success(`Created ${tag} at HEAD.`);
12724
- if (opts.push !== false) {
12725
- await exec4("git", ["push"], { cwd: root });
12726
- await exec4("git", ["push", "origin", tag], { cwd: root });
12727
- ui.success(`Pushed branch and ${tag}.`);
12728
- ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
12729
- }
12805
+ ui.success(`Shipped ${tag} \u2014 CI green. npm publication stays a human step (\`pnpm run publish:all\`).`);
12730
12806
  });
12731
12807
  }
12732
12808
 
@@ -12757,7 +12833,7 @@ import {
12757
12833
  findProjectRoot as findProjectRoot39,
12758
12834
  isRetiredMemory as isRetiredMemory3,
12759
12835
  judgeProposedSensor,
12760
- loadConfig as loadConfig13,
12836
+ loadConfig as loadConfig14,
12761
12837
  loadSensorLedger as loadSensorLedger4,
12762
12838
  loadMemoriesFromDir as loadMemoriesFromDir16,
12763
12839
  normalizeFramework,
@@ -12769,6 +12845,7 @@ import {
12769
12845
  buildProposeCommand,
12770
12846
  scaffoldPostIncidentTest,
12771
12847
  incidentHintsFromDiff,
12848
+ normalizeScaffoldStyle,
12772
12849
  selectCommandSensors as selectCommandSensors2,
12773
12850
  TEST_FRAMEWORKS,
12774
12851
  sensorPatternBrittleness as sensorPatternBrittleness2,
@@ -12880,7 +12957,7 @@ function registerSensors(program2) {
12880
12957
  }
12881
12958
  }
12882
12959
  }
12883
- const config = await loadConfig13(paths);
12960
+ const config = await loadConfig14(paths);
12884
12961
  const runCommands = opts.commands || config.enforcement?.runCommandSensors === true;
12885
12962
  const changedPaths = targets.map((t) => t.path).filter(Boolean);
12886
12963
  const allSensorMemories = await runnableSensorMemories(paths, false);
@@ -13100,7 +13177,7 @@ function registerSensors(program2) {
13100
13177
  }
13101
13178
  }
13102
13179
  const root2 = findProjectRoot39(opts.dir);
13103
- const { proposeSensor } = await import("./server-LNSXYVVW.js");
13180
+ const { proposeSensor } = await import("./server-Z4M6ERWN.js");
13104
13181
  const out = await proposeSensor(
13105
13182
  {
13106
13183
  memory_id: id,
@@ -13227,6 +13304,12 @@ function registerSensors(program2) {
13227
13304
  ).argument("<memory-id>", "lesson id to scaffold a test from").option("--framework <fw>", `test framework: ${TEST_FRAMEWORKS.join(" | ")} (auto-detected when omitted)`).option("--out <path>", "override the generated test file path (project-relative)").option("--stdout", "print the test to stdout instead of writing a file", false).option("--force", "overwrite an existing file at the target path", false).option(
13228
13305
  "--red-ref <ref>",
13229
13306
  "pre-fix incident commit/ref: the scaffold names the symbols the fix (<ref>..HEAD) touched and pre-fills the example around them, so the assertion is a targeted edit, not a blank page"
13307
+ ).option(
13308
+ "--style <style>",
13309
+ "test shape: example (default) | property (state the invariant once, checked over many inputs via fast-check/Hypothesis) | differential (assert the subject agrees with a --reference impl)"
13310
+ ).option(
13311
+ "--reference <import>",
13312
+ "differential style only: import specifier of the reference implementation to compare against (e.g. ../legacy/refund or a package name)"
13230
13313
  ).option("-d, --dir <dir>", "project root").action(async (id, opts) => {
13231
13314
  const root = findProjectRoot39(opts.dir);
13232
13315
  const paths = resolveHaivePaths36(root);
@@ -13244,6 +13327,17 @@ function registerSensors(program2) {
13244
13327
  process.exitCode = 1;
13245
13328
  return;
13246
13329
  }
13330
+ const style = opts.style ? normalizeScaffoldStyle(opts.style) : "example";
13331
+ if (opts.style && !style) {
13332
+ ui.error(`Unknown --style "${opts.style}". Use one of: example, property, differential.`);
13333
+ process.exitCode = 1;
13334
+ return;
13335
+ }
13336
+ if (style === "differential" && !opts.reference) {
13337
+ ui.error("--style differential requires --reference <import> (the reference implementation to compare against).");
13338
+ process.exitCode = 1;
13339
+ return;
13340
+ }
13247
13341
  const allGroups = await detectTestFrameworksForAnchors(root, fm.anchor.paths ?? []);
13248
13342
  const groups = opts.out ? allGroups.slice(0, 1) : allGroups;
13249
13343
  const fields = parseLessonFields(found.memory.body);
@@ -13270,14 +13364,15 @@ function registerSensors(program2) {
13270
13364
  paths: fm.anchor.paths,
13271
13365
  incidentHints
13272
13366
  };
13367
+ const styleOpts = { style: style ?? "example", reference: opts.reference };
13273
13368
  let scaffolds = groups.map(
13274
- (g) => scaffoldPostIncidentTest(lesson, { framework: forced ?? g.framework, outPath: opts.out, baseDir: g.baseDir })
13369
+ (g) => scaffoldPostIncidentTest(lesson, { framework: forced ?? g.framework, outPath: opts.out, baseDir: g.baseDir, ...styleOpts })
13275
13370
  );
13276
13371
  let proposeCmd = scaffolds[0].proposeCommand;
13277
13372
  if (scaffolds.length > 1) {
13278
13373
  proposeCmd = buildProposeCommand(lesson, scaffolds.map((s) => s.runCommand).join(" && "));
13279
13374
  scaffolds = groups.map(
13280
- (g) => scaffoldPostIncidentTest(lesson, { framework: forced ?? g.framework, baseDir: g.baseDir, proposeCommandOverride: proposeCmd })
13375
+ (g) => scaffoldPostIncidentTest(lesson, { framework: forced ?? g.framework, baseDir: g.baseDir, proposeCommandOverride: proposeCmd, ...styleOpts })
13281
13376
  );
13282
13377
  }
13283
13378
  if (opts.stdout) {
@@ -13697,7 +13792,7 @@ import "commander";
13697
13792
  import {
13698
13793
  buildDashboard,
13699
13794
  findProjectRoot as findProjectRoot41,
13700
- loadConfig as loadConfig14,
13795
+ loadConfig as loadConfig15,
13701
13796
  loadMemoriesFromDir as loadMemoriesFromDir18,
13702
13797
  loadPreventionEvents as loadPreventionEvents4,
13703
13798
  loadUsageIndex as loadUsageIndex16,
@@ -13717,7 +13812,7 @@ function registerDashboard(program2) {
13717
13812
  const memories = existsSync46(paths.memoriesDir) ? await loadMemoriesFromDir18(paths.memoriesDir) : [];
13718
13813
  const usage = await loadUsageIndex16(paths);
13719
13814
  const preventionEvents = await loadPreventionEvents4(paths);
13720
- const config = await loadConfig14(paths);
13815
+ const config = await loadConfig15(paths);
13721
13816
  const top = Math.max(1, Number.parseInt(opts.top ?? "10", 10) || 10);
13722
13817
  const dormantDays = opts.dormantDays ? Number.parseInt(opts.dormantDays, 10) : void 0;
13723
13818
  const report = buildDashboard(memories, usage, {
@@ -13848,7 +13943,7 @@ import { findProjectRoot as findProjectRoot42 } from "@hivelore/core";
13848
13943
  var exec6 = promisify8(execFile8);
13849
13944
  function registerDevLink(program2) {
13850
13945
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
13851
- 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) => {
13946
+ 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) => {
13852
13947
  const root = findProjectRoot42(opts.dir);
13853
13948
  if (!existsSync47(path44.join(root, "packages", "cli", "dist", "index.js"))) {
13854
13949
  ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
@@ -13861,9 +13956,9 @@ function registerDevLink(program2) {
13861
13956
  } catch {
13862
13957
  globalModules = path44.join(path44.dirname(path44.dirname(process.execPath)), "lib", "node_modules");
13863
13958
  }
13864
- const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path44.join(globalModules, scope)).filter((dir) => existsSync47(dir));
13959
+ const scopeDirs = ["@hivelore"].map((scope) => path44.join(globalModules, scope)).filter((dir) => existsSync47(dir));
13865
13960
  if (scopeDirs.length === 0) {
13866
- ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
13961
+ ui.error(`No global @hivelore install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
13867
13962
  process.exitCode = 1;
13868
13963
  return;
13869
13964
  }
@@ -14149,7 +14244,7 @@ function registerBridges(program2) {
14149
14244
 
14150
14245
  // src/index.ts
14151
14246
  var program = new Command48();
14152
- program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.47.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
14247
+ program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.51.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
14153
14248
  registerInit(program);
14154
14249
  registerResolveProject(program);
14155
14250
  registerEnforce(program);