@hivelore/cli 0.30.1 → 0.31.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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command64 } from "commander";
4
+ import { Command as Command65 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync as existsSync3 } from "fs";
@@ -13,6 +13,7 @@ import {
13
13
  compactAutoRecapBody,
14
14
  extractActionsBriefBody,
15
15
  findProjectRoot as findProjectRoot2,
16
+ inferModulesFromPaths,
16
17
  literalMatchesAllTokens,
17
18
  literalMatchesAnyToken,
18
19
  loadCodeMap as loadCodeMap3,
@@ -214,7 +215,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
214
215
  if (!f) continue;
215
216
  counts.set(f, (counts.get(f) ?? 0) + 1);
216
217
  }
217
- let entries = [...counts.entries()].map(([path63, changes]) => ({ path: path63, changes }));
218
+ let entries = [...counts.entries()].map(([path64, changes]) => ({ path: path64, changes }));
218
219
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
219
220
  if (lowerPaths.length > 0) {
220
221
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -757,8 +758,15 @@ async function refreshCodeMap(root, paths, force) {
757
758
  async function refreshCodeSearchIndex(paths) {
758
759
  try {
759
760
  const mod = await import("@hivelore/embeddings");
761
+ const cold = !existsSync2(mod.codeIndexPath(paths));
762
+ if (cold) {
763
+ console.error(
764
+ "[hivelore] Building the semantic code index (one-time \u2014 large repos can take a minute). Subsequent briefings reuse the cache."
765
+ );
766
+ }
760
767
  const embedder = await mod.Embedder.create();
761
768
  const { report } = await mod.rebuildCodeIndex(paths, embedder);
769
+ if (cold) console.error("[hivelore] Semantic code index ready.");
762
770
  return report.added > 0 || report.updated > 0 || report.removed > 0;
763
771
  } catch {
764
772
  return false;
@@ -1062,11 +1070,21 @@ function registerBriefing(program2) {
1062
1070
  const usefulCount = priorities.filter((p) => p === "useful").length;
1063
1071
  const backgroundCount = priorities.filter((p) => p === "background").length;
1064
1072
  const quality = mustReadCount > 0 || usefulCount > 0 ? mustReadCount === 0 && backgroundCount > usefulCount && backgroundCount > 2 ? "noisy" : "strong" : "thin";
1073
+ const inferredModules = inferModulesFromPaths(filePaths);
1074
+ const moduleContexts = [];
1075
+ for (const m of inferredModules) {
1076
+ const ctxFile = path4.join(paths.modulesContextDir, m, "context.md");
1077
+ if (existsSync3(ctxFile)) {
1078
+ moduleContexts.push({ name: m, content: (await readFile2(ctxFile, "utf8")).trim() });
1079
+ }
1080
+ }
1065
1081
  if (json) {
1066
1082
  console.log(JSON.stringify({
1067
1083
  task: opts.task ?? null,
1068
1084
  files: filePaths,
1069
1085
  briefing_quality: quality,
1086
+ inferred_modules: inferredModules,
1087
+ module_contexts: moduleContexts,
1070
1088
  counts: { must_read: mustReadCount, useful: usefulCount, background: backgroundCount },
1071
1089
  recap_id: recaps[0]?.memory.frontmatter.id ?? null,
1072
1090
  memories: top.map((item, i) => ({
@@ -1084,6 +1102,12 @@ function registerBriefing(program2) {
1084
1102
  }
1085
1103
  out(ui.dim(`briefing_quality: ${quality} \xB7 must_read=${mustReadCount} useful=${usefulCount} background=${backgroundCount}`));
1086
1104
  out("");
1105
+ for (const mc of moduleContexts) {
1106
+ if (stopped()) break;
1107
+ out(ui.bold(`=== Module context: ${mc.name} ===`));
1108
+ out(mc.content);
1109
+ out("");
1110
+ }
1087
1111
  printCliBreadcrumbs({
1088
1112
  top,
1089
1113
  priorities,
@@ -3622,12 +3646,18 @@ async function seedStackPack(haivePaths, stack) {
3622
3646
  const DATE_PREFIX = /^\d{4}-\d{2}-\d{2}-/;
3623
3647
  const existingTopics = /* @__PURE__ */ new Set();
3624
3648
  const existingSignatures = /* @__PURE__ */ new Set();
3649
+ const handWrittenSlugs = [];
3625
3650
  if (existsSync10(haivePaths.memoriesDir)) {
3626
3651
  for (const { memory: memory2 } of await loadMemoriesFromDir5(haivePaths.memoriesDir)) {
3627
3652
  if (memory2.frontmatter.topic) existingTopics.add(memory2.frontmatter.topic);
3628
3653
  existingSignatures.add(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
3654
+ if (!memory2.frontmatter.tags.includes("stack-pack")) {
3655
+ handWrittenSlugs.push(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
3656
+ }
3629
3657
  }
3630
3658
  }
3659
+ const overlapHints = [];
3660
+ const slugTokens = (slug) => new Set(slug.split("-").filter((t) => t.length > 3 && !["convention", "decision", "gotcha", "attempt", "architecture"].includes(t)));
3631
3661
  let memCount = 0;
3632
3662
  let sensorCount = 0;
3633
3663
  for (const mem of memories) {
@@ -3636,7 +3666,11 @@ async function seedStackPack(haivePaths, stack) {
3636
3666
  kind: "regex",
3637
3667
  pattern: mem.sensor.pattern,
3638
3668
  ...mem.sensor.flags ? { flags: mem.sensor.flags } : {},
3639
- paths: mem.sensor.paths ?? [],
3669
+ // Stack rules are stack-wide: when the pack doesn't scope the sensor itself, pin an
3670
+ // explicit repo-wide glob. Leaving [] lets sensorAppliesToPath fall back to the MEMORY's
3671
+ // anchor paths — and seeds get anchored to one exemplar file later, silently shrinking
3672
+ // "never $disconnect() in serverless" to a single file (found in the 0.30.0 field test).
3673
+ paths: mem.sensor.paths ?? ["**"],
3640
3674
  message: mem.sensor.message,
3641
3675
  severity: "warn",
3642
3676
  autogen: false,
@@ -3674,13 +3708,28 @@ ${SEED_FOOTER(stack)}` });
3674
3708
  existingSignatures.add(signature);
3675
3709
  memCount++;
3676
3710
  if (sensor) sensorCount++;
3711
+ const seededTokens = slugTokens(combinedSlug);
3712
+ for (const prior of handWrittenSlugs) {
3713
+ const shared = [...slugTokens(prior)].filter((t) => seededTokens.has(t));
3714
+ if (shared.length >= 2) {
3715
+ overlapHints.push({ seeded: fm.id, existing: prior });
3716
+ break;
3717
+ }
3718
+ }
3719
+ }
3720
+ if (overlapHints.length > 0) {
3721
+ ui.warn(`${overlapHints.length} seeded lesson(s) may duplicate existing hand-written memories:`);
3722
+ for (const h of overlapHints.slice(0, 5)) {
3723
+ ui.info(` ${h.seeded} \u2194 ${h.existing}`);
3724
+ }
3725
+ ui.info(" Review with `hivelore memory conflict-candidates`, then `hivelore memory resolve-conflict`.");
3677
3726
  }
3678
3727
  return { memories: memCount, sensors: sensorCount };
3679
3728
  }
3680
3729
 
3681
3730
  // src/commands/init.ts
3682
3731
  var execFileAsync = promisify2(execFile2);
3683
- var HAIVE_GITHUB_ACTION_REF = `v${"0.30.1"}`;
3732
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.31.0"}`;
3684
3733
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
3685
3734
 
3686
3735
  > Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
@@ -4899,7 +4948,7 @@ import path42 from "path";
4899
4948
  import {
4900
4949
  deriveConfidence as deriveConfidence2,
4901
4950
  getUsage as getUsage3,
4902
- inferModulesFromPaths,
4951
+ inferModulesFromPaths as inferModulesFromPaths2,
4903
4952
  loadMemoriesFromDir as loadMemoriesFromDir7,
4904
4953
  loadUsageIndex as loadUsageIndex4,
4905
4954
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
@@ -4940,27 +4989,27 @@ import {
4940
4989
  serializeMemory as serializeMemory6
4941
4990
  } from "@hivelore/core";
4942
4991
  import { z as z14 } from "zod";
4943
- import { mkdir as mkdir32, writeFile as writeFile82 } from "fs/promises";
4944
- import { existsSync as existsSync152 } from "fs";
4945
- import path52 from "path";
4992
+ import { mkdir as mkdir32, writeFile as writeFile92 } from "fs/promises";
4993
+ import { existsSync as existsSync16 } from "fs";
4994
+ import path62 from "path";
4946
4995
  import {
4947
4996
  buildFrontmatter as buildFrontmatter22,
4948
4997
  memoryFilePath as memoryFilePath22,
4949
- serializeMemory as serializeMemory7,
4998
+ serializeMemory as serializeMemory8,
4950
4999
  suggestSensorSeed as suggestSensorSeed2
4951
5000
  } from "@hivelore/core";
4952
- import { z as z15 } from "zod";
5001
+ import { z as z16 } from "zod";
4953
5002
  import { execSync } from "child_process";
4954
- import { readFile as readFile32, writeFile as writeFile92 } from "fs/promises";
4955
- import { existsSync as existsSync16 } from "fs";
4956
- import path62 from "path";
5003
+ import { readFile as readFile32, writeFile as writeFile82 } from "fs/promises";
5004
+ import { existsSync as existsSync152 } from "fs";
5005
+ import path52 from "path";
4957
5006
  import {
4958
5007
  extractSensorExamples,
4959
5008
  judgeProposedSensor,
4960
5009
  loadMemoriesFromDir as loadMemoriesFromDir13,
4961
- serializeMemory as serializeMemory8
5010
+ serializeMemory as serializeMemory7
4962
5011
  } from "@hivelore/core";
4963
- import { z as z16 } from "zod";
5012
+ import { z as z15 } from "zod";
4964
5013
  import { existsSync as existsSync17 } from "fs";
4965
5014
  import { mkdir as mkdir42, readFile as readFile42, writeFile as writeFile102 } from "fs/promises";
4966
5015
  import path72 from "path";
@@ -5019,7 +5068,7 @@ import {
5019
5068
  compactAutoRecapBody as compactAutoRecapBody2,
5020
5069
  extractActionsBriefBody as extractActionsBriefBody2,
5021
5070
  getUsage as getUsage6,
5022
- inferModulesFromPaths as inferModulesFromPaths2,
5071
+ inferModulesFromPaths as inferModulesFromPaths22,
5023
5072
  isAutoPromoteEligible,
5024
5073
  isDecaying,
5025
5074
  isRetiredMemory as isRetiredMemory2,
@@ -5832,7 +5881,7 @@ var MemForFilesInputSchema = {
5832
5881
  track: z9.boolean().default(true).describe("Increment read_count on returned memories")
5833
5882
  };
5834
5883
  async function memForFiles(input, ctx) {
5835
- const inferred = inferModulesFromPaths(input.files);
5884
+ const inferred = inferModulesFromPaths2(input.files);
5836
5885
  if (!existsSync92(ctx.paths.memoriesDir)) {
5837
5886
  return {
5838
5887
  inferred_modules: inferred,
@@ -6138,71 +6187,17 @@ async function memApprove(input, ctx) {
6138
6187
  file_path: found.filePath
6139
6188
  };
6140
6189
  }
6141
- var MemTriedInputSchema = {
6142
- what: z15.string().min(1).describe("Brief description of the approach that was tried"),
6143
- why_failed: z15.string().min(1).describe("Why it failed or why it should NOT be used"),
6144
- instead: z15.string().optional().describe("What to use or do instead (recommended alternative)"),
6145
- scope: z15.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
6146
- module: z15.string().optional().describe("Module name (required when scope=module)"),
6147
- tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
6148
- paths: z15.array(z15.string()).default([]).describe("Anchor file paths this applies to"),
6149
- author: z15.string().optional().describe("Author handle or email")
6150
- };
6151
- async function memTried(input, ctx) {
6152
- if (!existsSync152(ctx.paths.haiveDir)) {
6153
- throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
6154
- }
6155
- const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
6156
- const baseFm = buildFrontmatter22({
6157
- type: "attempt",
6158
- slug,
6159
- scope: input.scope,
6160
- module: input.module,
6161
- tags: input.tags,
6162
- paths: input.paths,
6163
- author: input.author
6164
- });
6165
- const frontmatter = { ...baseFm, status: "validated" };
6166
- const lines = [`# ${input.what}`, ""];
6167
- lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
6168
- if (input.instead) {
6169
- lines.push("", `**Instead, use:** ${input.instead}`);
6170
- }
6171
- const body = lines.join("\n") + "\n";
6172
- const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
6173
- await mkdir32(path52.dirname(file), { recursive: true });
6174
- if (existsSync152(file)) {
6175
- throw new Error(`Memory already exists at ${file}`);
6176
- }
6177
- await writeFile82(file, serializeMemory7({ frontmatter, body }), "utf8");
6178
- const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
6179
- const hint = input.paths.length === 0 ? "No `paths` given, so this attempt is feedforward-only \u2014 it will be briefed but the gate cannot block the repeat. Re-run with `paths` set to the file(s) where the mistake lives, then call propose_sensor to close the loop." : seed ? "This attempt is NOT yet enforced. Call propose_sensor to turn it into a reliable block \u2014 a candidate is pre-filled in proposed_sensor_seed (refine it: pattern = the faulty usage, absent = the correct-usage marker). Hivelore validates the proposal (silent on current code, fires on the bad example) before trusting it to block." : "This attempt is NOT yet enforced and no candidate pattern could be derived from the wording. Call propose_sensor with a discriminating pattern (pattern = faulty usage, absent = correct-usage marker) to close the loop.";
6180
- return {
6181
- id: frontmatter.id,
6182
- scope: frontmatter.scope,
6183
- file_path: file,
6184
- loop_open: true,
6185
- ...seed ? {
6186
- proposed_sensor_seed: {
6187
- pattern: seed.pattern,
6188
- ...seed.absent ? { absent: seed.absent } : {},
6189
- message: seed.message
6190
- }
6191
- } : {},
6192
- hint
6193
- };
6194
- }
6195
6190
  var ProposeSensorInputSchema = {
6196
- memory_id: z16.string().min(1).describe("Id of the gotcha/attempt memory this sensor protects."),
6197
- pattern: z16.string().min(1).describe("Regex matching the FAULTY usage (the risky call/token), e.g. 'stripe\\.paymentIntents\\.create'."),
6198
- absent: z16.string().optional().describe(
6191
+ memory_id: z15.string().min(1).describe("Id of the gotcha/attempt memory this sensor protects."),
6192
+ pattern: z15.string().min(1).describe("Regex matching the FAULTY usage (the risky call/token), e.g. 'stripe\\.paymentIntents\\.create'."),
6193
+ absent: z15.string().optional().describe(
6199
6194
  "Regex for the CORRECT-usage marker (e.g. 'idempotencyKey'). When it appears in the window around a match, the catch is suppressed \u2014 this is what makes the sensor discriminate the faulty call from the correct one. STRONGLY recommended for 'X without Y' lessons."
6200
6195
  ),
6201
- bad_example: z16.string().optional().describe("A code snippet that SHOULD match \u2014 proves the sensor catches the mistake. If omitted, examples are read from the lesson body."),
6202
- severity: z16.enum(["warn", "block"]).default("block").describe("block = hard-fail the gate (accepted ONLY if it passes self-validation). warn = advisory."),
6203
- message: z16.string().optional().describe("LLM-facing fix message shown when it fires. Defaults to one derived from the lesson."),
6204
- flags: z16.string().optional().describe("Optional regex flags (e.g. 'i' for case-insensitive)."),
6205
- paths: z16.array(z16.string()).default([]).describe("Override scope paths. Defaults to the memory's anchor paths.")
6196
+ bad_example: z15.string().optional().describe("A code snippet that SHOULD match \u2014 proves the sensor catches the mistake. If omitted, examples are read from the lesson body."),
6197
+ severity: z15.enum(["warn", "block"]).default("block").describe("block = hard-fail the gate (accepted ONLY if it passes self-validation). warn = advisory."),
6198
+ message: z15.string().optional().describe("LLM-facing fix message shown when it fires. Defaults to one derived from the lesson."),
6199
+ flags: z15.string().optional().describe("Optional regex flags (e.g. 'i' for case-insensitive)."),
6200
+ paths: z15.array(z15.string()).default([]).describe("Override scope paths. Defaults to the memory's anchor paths.")
6206
6201
  };
6207
6202
  function deriveMessage(body, pattern, absent) {
6208
6203
  const instead = body.match(/\*\*Instead,\s*use:\*\*\s*([^\n]+)/i)?.[1]?.trim();
@@ -6227,8 +6222,8 @@ async function readPresumedCorrectTargets(root, relPaths) {
6227
6222
  continue;
6228
6223
  } catch {
6229
6224
  }
6230
- const abs = path62.resolve(root, rel);
6231
- if (!existsSync16(abs)) continue;
6225
+ const abs = path52.resolve(root, rel);
6226
+ if (!existsSync152(abs)) continue;
6232
6227
  try {
6233
6228
  targets.push({ path: rel, content: await readFile32(abs, "utf8") });
6234
6229
  } catch {
@@ -6237,7 +6232,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
6237
6232
  return targets;
6238
6233
  }
6239
6234
  async function proposeSensor(input, ctx) {
6240
- if (!existsSync16(ctx.paths.memoriesDir)) {
6235
+ if (!existsSync152(ctx.paths.memoriesDir)) {
6241
6236
  throw new Error(`No .ai/memories at ${ctx.paths.root}. Run 'hivelore init' first.`);
6242
6237
  }
6243
6238
  try {
@@ -6297,7 +6292,7 @@ async function proposeSensor(input, ctx) {
6297
6292
  frontmatter: { ...found.memory.frontmatter, sensor },
6298
6293
  body: found.memory.body
6299
6294
  };
6300
- await writeFile92(found.filePath, serializeMemory8(next), "utf8");
6295
+ await writeFile82(found.filePath, serializeMemory7(next), "utf8");
6301
6296
  return {
6302
6297
  accepted: true,
6303
6298
  memory_id: input.memory_id,
@@ -6306,6 +6301,97 @@ async function proposeSensor(input, ctx) {
6306
6301
  file_path: found.filePath
6307
6302
  };
6308
6303
  }
6304
+ var MemTriedInputSchema = {
6305
+ what: z16.string().min(1).describe("Brief description of the approach that was tried"),
6306
+ why_failed: z16.string().min(1).describe("Why it failed or why it should NOT be used"),
6307
+ instead: z16.string().optional().describe("What to use or do instead (recommended alternative)"),
6308
+ scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
6309
+ module: z16.string().optional().describe("Module name (required when scope=module)"),
6310
+ tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
6311
+ paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
6312
+ author: z16.string().optional().describe("Author handle or email"),
6313
+ sensor: z16.object({
6314
+ pattern: z16.string().min(1).describe("Regex matching the FAULTY usage (added diff lines)"),
6315
+ absent: z16.string().optional().describe("Regex marking CORRECT usage nearby \u2014 excludes it from firing"),
6316
+ severity: z16.enum(["warn", "block"]).default("block").describe("block = deterministic gate refusal"),
6317
+ message: z16.string().optional().describe("Self-correction message shown when the sensor fires"),
6318
+ bad_example: z16.string().optional().describe("Code snippet the sensor MUST fire on (validation)")
6319
+ }).optional().describe(
6320
+ "ONE-SHOT loop close: validate and attach a sensor in the same call (equivalent to a follow-up propose_sensor). Validated against HEAD \u2014 silent on current code, fires on the bad example. If rejected, the attempt is still saved and the verdict tells you how to revise."
6321
+ )
6322
+ };
6323
+ async function memTried(input, ctx) {
6324
+ if (!existsSync16(ctx.paths.haiveDir)) {
6325
+ throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
6326
+ }
6327
+ const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
6328
+ const baseFm = buildFrontmatter22({
6329
+ type: "attempt",
6330
+ slug,
6331
+ scope: input.scope,
6332
+ module: input.module,
6333
+ tags: input.tags,
6334
+ paths: input.paths,
6335
+ author: input.author
6336
+ });
6337
+ const frontmatter = { ...baseFm, status: "validated" };
6338
+ const lines = [`# ${input.what}`, ""];
6339
+ lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
6340
+ if (input.instead) {
6341
+ lines.push("", `**Instead, use:** ${input.instead}`);
6342
+ }
6343
+ const body = lines.join("\n") + "\n";
6344
+ const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
6345
+ await mkdir32(path62.dirname(file), { recursive: true });
6346
+ if (existsSync16(file)) {
6347
+ throw new Error(`Memory already exists at ${file}`);
6348
+ }
6349
+ await writeFile92(file, serializeMemory8({ frontmatter, body }), "utf8");
6350
+ if (input.sensor) {
6351
+ const verdict = await proposeSensor(
6352
+ {
6353
+ memory_id: frontmatter.id,
6354
+ pattern: input.sensor.pattern,
6355
+ absent: input.sensor.absent,
6356
+ severity: input.sensor.severity ?? "block",
6357
+ message: input.sensor.message,
6358
+ bad_example: input.sensor.bad_example,
6359
+ flags: void 0,
6360
+ paths: []
6361
+ },
6362
+ ctx
6363
+ );
6364
+ return {
6365
+ id: frontmatter.id,
6366
+ scope: frontmatter.scope,
6367
+ file_path: file,
6368
+ loop_open: !verdict.accepted,
6369
+ sensor_result: {
6370
+ accepted: verdict.accepted,
6371
+ severity: input.sensor.severity ?? "block",
6372
+ ...verdict.reason ? { reason: verdict.reason } : {},
6373
+ ...verdict.guidance ? { guidance: verdict.guidance } : {}
6374
+ },
6375
+ hint: verdict.accepted ? "Loop closed: the attempt is saved AND enforced \u2014 the gate now refuses a repeat deterministically." : `Attempt saved, but the sensor was rejected (${verdict.reason}). Revise per the guidance and re-propose with propose_sensor.`
6376
+ };
6377
+ }
6378
+ const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
6379
+ const hint = input.paths.length === 0 ? "No `paths` given, so this attempt is feedforward-only \u2014 it will be briefed but the gate cannot block the repeat. Re-run with `paths` set to the file(s) where the mistake lives, then call propose_sensor to close the loop." : seed ? "This attempt is NOT yet enforced. Call propose_sensor (or re-run mem_tried with the one-shot `sensor` parameter) to turn it into a reliable block \u2014 a candidate is pre-filled in proposed_sensor_seed (refine it: pattern = the faulty usage, absent = the correct-usage marker). Hivelore validates the proposal (silent on current code, fires on the bad example) before trusting it to block." : "This attempt is NOT yet enforced and no candidate pattern could be derived from the wording. Call propose_sensor with a discriminating pattern (pattern = faulty usage, absent = correct-usage marker) to close the loop.";
6380
+ return {
6381
+ id: frontmatter.id,
6382
+ scope: frontmatter.scope,
6383
+ file_path: file,
6384
+ loop_open: true,
6385
+ ...seed ? {
6386
+ proposed_sensor_seed: {
6387
+ pattern: seed.pattern,
6388
+ ...seed.absent ? { absent: seed.absent } : {},
6389
+ message: seed.message
6390
+ }
6391
+ } : {},
6392
+ hint
6393
+ };
6394
+ }
6309
6395
  var IngestFindingsInputSchema = {
6310
6396
  format: z17.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
6311
6397
  report_path: z17.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
@@ -6886,7 +6972,7 @@ async function getBriefing(input, ctx) {
6886
6972
  const briefingMaxTokens = resolvedBudget.max_tokens;
6887
6973
  const briefingMaxMemories = resolvedBudget.max_memories;
6888
6974
  const briefingIncludeModules = resolvedBudget.include_module_contexts;
6889
- const inferred = inferModulesFromPaths2(input.files);
6975
+ const inferred = inferModulesFromPaths22(input.files);
6890
6976
  const memories = [];
6891
6977
  let searchMode = "literal";
6892
6978
  let usage = { version: 1, updated_at: "", by_id: {} };
@@ -7196,11 +7282,20 @@ ${m.content}`).join("\n\n---\n\n"),
7196
7282
  if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
7197
7283
  }
7198
7284
  const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({ ...m, body: extractActionsBriefBody2(m.body) })) : trimmedMemories;
7199
- const outputMemories = formattedMemories.map((m) => ({
7285
+ let outputMemories = formattedMemories.map((m) => ({
7200
7286
  ...m,
7201
7287
  priority: classifyMemoryPriority2(m, byId.get(m.id), input.files, input.symbols),
7202
7288
  why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
7203
7289
  }));
7290
+ if (input.format === "full") {
7291
+ const hasDirectHits = outputMemories.some((m) => m.priority === "must_read" || m.priority === "useful");
7292
+ if (hasDirectHits) {
7293
+ outputMemories = outputMemories.map(
7294
+ (m) => m.priority === "background" ? { ...m, body: `${compactSummary(m.body)}
7295
+ (background \u2014 full body: mem_get("${m.id}"))` } : m
7296
+ );
7297
+ }
7298
+ }
7204
7299
  const briefingQuality = classifyBriefingQuality(outputMemories, {
7205
7300
  isTemplateContext,
7206
7301
  autoContextGenerated,
@@ -7515,7 +7610,7 @@ function oneLine(value) {
7515
7610
  return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
7516
7611
  }
7517
7612
  function serverVersion() {
7518
- return true ? "0.30.1" : "dev";
7613
+ return true ? "0.31.0" : "dev";
7519
7614
  }
7520
7615
  var CodeMapInputSchema = {
7521
7616
  file: z21.string().optional().describe("Filter to files whose path contains this substring"),
@@ -9426,7 +9521,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
9426
9521
  };
9427
9522
  }
9428
9523
  var SERVER_NAME = "hivelore";
9429
- var SERVER_VERSION = "0.30.1";
9524
+ var SERVER_VERSION = "0.31.0";
9430
9525
  function jsonResult(data) {
9431
9526
  return {
9432
9527
  content: [
@@ -11972,35 +12067,20 @@ function registerMemoryHot(memory2) {
11972
12067
  }
11973
12068
 
11974
12069
  // src/commands/memory-tried.ts
11975
- import { mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
11976
12070
  import { existsSync as existsSync45 } from "fs";
11977
12071
  import path28 from "path";
11978
12072
  import "commander";
11979
12073
  import {
11980
- buildFrontmatter as buildFrontmatter8,
11981
12074
  findProjectRoot as findProjectRoot22,
11982
- memoryFilePath as memoryFilePath9,
11983
- resolveHaivePaths as resolveHaivePaths19,
11984
- serializeMemory as serializeMemory20,
11985
- suggestSensorSeed as suggestSensorSeed4
12075
+ resolveHaivePaths as resolveHaivePaths19
11986
12076
  } from "@hivelore/core";
12077
+ function parseCsv4(raw) {
12078
+ return (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
12079
+ }
11987
12080
  function registerMemoryTried(memory2) {
11988
12081
  memory2.command("tried").description(
11989
- `Record a FAILED approach \u2014 prevents repeated mistakes in future sessions.
11990
-
11991
- This is the most valuable type of negative knowledge. It surfaces FIRST in
11992
- get_briefing so agents can't miss it. Auto-validated (no approval cycle).
11993
-
11994
- Use this immediately when you try something and it fails.
11995
-
11996
- Example:
11997
- hivelore memory tried \\\\
11998
- --what "importing X with ESM dynamic import" \\\\
11999
- --why-failed "tsup bundles it as CJS, dynamic import fails at runtime" \\\\
12000
- --instead "use static import in the entry file" \\\\
12001
- --paths packages/cli/src/index.ts
12002
- `
12003
- ).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
12082
+ 'Record a FAILED approach \u2014 prevents repeated mistakes in future sessions.\n\n This is the most valuable type of negative knowledge. It surfaces FIRST in\n get_briefing so agents can\'t miss it. Auto-validated (no approval cycle).\n\n Use this immediately when you try something and it fails.\n\n One-shot loop close: add --sensor-pattern to validate and attach the guardrail\n in the same command (equivalent to a follow-up `sensors propose`).\n\n Example:\n hivelore memory tried \\\\\n --what "importing X with ESM dynamic import" \\\\\n --why-failed "tsup bundles it as CJS, dynamic import fails at runtime" \\\\\n --instead "use static import in the entry file" \\\\\n --paths packages/cli/src/index.ts \\\\\n --sensor-pattern "await import\\\\(" --sensor-absent "static import"\n'
12083
+ ).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("--sensor-pattern <regex>", "one-shot: regex matching the FAULTY usage \u2014 validates + attaches a sensor in this call").option("--sensor-absent <regex>", "one-shot: regex marking CORRECT usage nearby (excludes it from firing)").option("--sensor-severity <level>", "one-shot sensor severity: warn | block", "block").option("--sensor-message <text>", "one-shot: self-correction message shown when the sensor fires").option("--bad-example <code>", "one-shot: code snippet the sensor must fire on (validation)").option("-d, --dir <dir>", "project root").action(async (opts) => {
12004
12084
  const root = findProjectRoot22(opts.dir);
12005
12085
  const paths = resolveHaivePaths19(root);
12006
12086
  if (!existsSync45(paths.haiveDir)) {
@@ -12008,52 +12088,56 @@ function registerMemoryTried(memory2) {
12008
12088
  process.exitCode = 1;
12009
12089
  return;
12010
12090
  }
12011
- const slug = opts.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
12012
- const baseFm = buildFrontmatter8({
12013
- type: "attempt",
12014
- slug,
12015
- scope: opts.scope,
12016
- module: opts.module,
12017
- tags: parseCsv4(opts.tags),
12018
- paths: parseCsv4(opts.paths ?? opts.files),
12019
- author: opts.author
12020
- });
12021
- const frontmatter = { ...baseFm, status: "validated" };
12022
- const lines = [`# ${opts.what}`, ""];
12023
- lines.push(`**Why it failed / do NOT use:** ${opts.whyFailed}`);
12024
- if (opts.instead) {
12025
- lines.push("", `**Instead, use:** ${opts.instead}`);
12026
- }
12027
- const body = lines.join("\n") + "\n";
12028
- const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
12029
- await mkdir14(path28.dirname(file), { recursive: true });
12030
- if (existsSync45(file)) {
12031
- ui.error(`Memory already exists at ${file}`);
12091
+ const severity = opts.sensorSeverity === "warn" ? "warn" : "block";
12092
+ let result;
12093
+ try {
12094
+ result = await memTried(
12095
+ {
12096
+ what: opts.what,
12097
+ why_failed: opts.whyFailed,
12098
+ instead: opts.instead,
12099
+ // "shared" is a legacy MemoryScope alias not accepted by mem_tried — normalize to team.
12100
+ scope: opts.scope === "shared" ? "team" : opts.scope ?? "personal",
12101
+ module: opts.module,
12102
+ tags: parseCsv4(opts.tags),
12103
+ paths: parseCsv4(opts.paths ?? opts.files),
12104
+ author: opts.author,
12105
+ sensor: opts.sensorPattern ? {
12106
+ pattern: opts.sensorPattern,
12107
+ absent: opts.sensorAbsent,
12108
+ severity,
12109
+ message: opts.sensorMessage,
12110
+ bad_example: opts.badExample
12111
+ } : void 0
12112
+ },
12113
+ { paths }
12114
+ );
12115
+ } catch (err) {
12116
+ ui.error(err instanceof Error ? err.message : String(err));
12032
12117
  process.exitCode = 1;
12033
12118
  return;
12034
12119
  }
12035
- await writeFile23(file, serializeMemory20({ frontmatter, body }), "utf8");
12036
- ui.success(`Recorded: ${path28.relative(root, file)}`);
12037
- ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
12038
- const seed = frontmatter.anchor.paths.length > 0 ? suggestSensorSeed4(body, frontmatter.anchor.paths) : null;
12039
- if (frontmatter.anchor.paths.length === 0) {
12040
- ui.warn("No --paths \u2014 lesson is briefed but NOT enforced. Add --paths, then call propose_sensor (MCP) to close the loop.");
12041
- } else if (seed) {
12042
- ui.warn("Lesson NOT yet enforced \u2014 close the loop with a validated sensor via propose_sensor (MCP).");
12120
+ ui.success(`Recorded: ${path28.relative(root, result.file_path)}`);
12121
+ ui.info(`id=${result.id} type=attempt status=validated (auto-approved)`);
12122
+ if (result.sensor_result) {
12123
+ if (result.sensor_result.accepted) {
12124
+ ui.success(`Loop closed: sensor attached (${result.sensor_result.severity}) \u2014 the gate now refuses a repeat deterministically.`);
12125
+ } else {
12126
+ ui.warn(`Attempt saved, but the sensor was rejected (${result.sensor_result.reason ?? "rejected"}).`);
12127
+ if (result.sensor_result.guidance) ui.info(` ${result.sensor_result.guidance}`);
12128
+ }
12129
+ return;
12130
+ }
12131
+ if (result.proposed_sensor_seed) {
12132
+ ui.warn("Lesson NOT yet enforced \u2014 close the loop with `--sensor-pattern` (one-shot) or propose_sensor (MCP).");
12043
12133
  ui.info(
12044
- ` candidate pattern=${JSON.stringify(seed.pattern)}` + (seed.absent ? ` absent=${JSON.stringify(seed.absent)}` : "") + " \u2014 refine, then validate (silent on current code, fires on the bad example)."
12045
- );
12046
- } else {
12047
- ui.warn(
12048
- "Lesson NOT yet enforced and no candidate pattern could be derived. Author a discriminating sensor (pattern = faulty usage, absent = correct-usage marker) via propose_sensor."
12134
+ ` candidate pattern=${JSON.stringify(result.proposed_sensor_seed.pattern)}` + (result.proposed_sensor_seed.absent ? ` absent=${JSON.stringify(result.proposed_sensor_seed.absent)}` : "") + " \u2014 refine, then validate (silent on current code, fires on the bad example)."
12049
12135
  );
12136
+ } else if (result.hint) {
12137
+ ui.warn(result.hint);
12050
12138
  }
12051
12139
  });
12052
12140
  }
12053
- function parseCsv4(value) {
12054
- if (!value) return [];
12055
- return value.split(",").map((s) => s.trim()).filter(Boolean);
12056
- }
12057
12141
 
12058
12142
  // src/commands/memory-seed.ts
12059
12143
  import { readFile as readFile14 } from "fs/promises";
@@ -12299,7 +12383,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
12299
12383
  }
12300
12384
 
12301
12385
  // src/commands/memory-reject.ts
12302
- import { writeFile as writeFile24 } from "fs/promises";
12386
+ import { writeFile as writeFile23 } from "fs/promises";
12303
12387
  import { existsSync as existsSync49 } from "fs";
12304
12388
  import "commander";
12305
12389
  import {
@@ -12308,7 +12392,7 @@ import {
12308
12392
  recordRejection as recordRejection3,
12309
12393
  resolveHaivePaths as resolveHaivePaths23,
12310
12394
  saveUsageIndex as saveUsageIndex4,
12311
- serializeMemory as serializeMemory21
12395
+ serializeMemory as serializeMemory20
12312
12396
  } from "@hivelore/core";
12313
12397
  function registerMemoryReject(memory2) {
12314
12398
  memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
@@ -12326,9 +12410,9 @@ function registerMemoryReject(memory2) {
12326
12410
  process.exitCode = 1;
12327
12411
  return;
12328
12412
  }
12329
- await writeFile24(
12413
+ await writeFile23(
12330
12414
  loaded.filePath,
12331
- serializeMemory21({
12415
+ serializeMemory20({
12332
12416
  frontmatter: {
12333
12417
  ...loaded.memory.frontmatter,
12334
12418
  status: "rejected",
@@ -12594,7 +12678,7 @@ function pad(value, width) {
12594
12678
 
12595
12679
  // src/commands/memory-feedback.ts
12596
12680
  import { existsSync as existsSync55 } from "fs";
12597
- import { writeFile as writeFile25 } from "fs/promises";
12681
+ import { writeFile as writeFile24 } from "fs/promises";
12598
12682
  import "commander";
12599
12683
  import {
12600
12684
  applyFeedbackAdjustment as applyFeedbackAdjustment2,
@@ -12607,7 +12691,7 @@ import {
12607
12691
  recommendFeedbackAdjustment as recommendFeedbackAdjustment2,
12608
12692
  resolveHaivePaths as resolveHaivePaths28,
12609
12693
  saveUsageIndex as saveUsageIndex6,
12610
- serializeMemory as serializeMemory23
12694
+ serializeMemory as serializeMemory21
12611
12695
  } from "@hivelore/core";
12612
12696
  function registerMemoryFeedback(memory2) {
12613
12697
  memory2.command("feedback <id>").description(
@@ -12642,7 +12726,7 @@ function registerMemoryFeedback(memory2) {
12642
12726
  const adjustedFrontmatter = applyFeedbackAdjustment2(target.memory.frontmatter, adjustment);
12643
12727
  if (adjustedFrontmatter !== target.memory.frontmatter) {
12644
12728
  target.memory.frontmatter = adjustedFrontmatter;
12645
- await writeFile25(target.filePath, serializeMemory23(target.memory), "utf8");
12729
+ await writeFile24(target.filePath, serializeMemory21(target.memory), "utf8");
12646
12730
  }
12647
12731
  const impact = computeImpact4(target.memory.frontmatter, usage);
12648
12732
  if (opts.json) {
@@ -12660,14 +12744,14 @@ function registerMemoryFeedback(memory2) {
12660
12744
  }
12661
12745
 
12662
12746
  // src/commands/memory-verify.ts
12663
- import { writeFile as writeFile26 } from "fs/promises";
12747
+ import { writeFile as writeFile25 } from "fs/promises";
12664
12748
  import { existsSync as existsSync56 } from "fs";
12665
12749
  import path36 from "path";
12666
12750
  import "commander";
12667
12751
  import {
12668
12752
  findProjectRoot as findProjectRoot32,
12669
12753
  resolveHaivePaths as resolveHaivePaths29,
12670
- serializeMemory as serializeMemory24,
12754
+ serializeMemory as serializeMemory23,
12671
12755
  verifyAnchor as verifyAnchor3
12672
12756
  } from "@hivelore/core";
12673
12757
  function registerMemoryVerify(memory2) {
@@ -12734,7 +12818,7 @@ function registerMemoryVerify(memory2) {
12734
12818
  }
12735
12819
  if (opts.update) {
12736
12820
  const next = applyVerification2(mem, result);
12737
- await writeFile26(filePath, serializeMemory24(next), "utf8");
12821
+ await writeFile25(filePath, serializeMemory23(next), "utf8");
12738
12822
  updated++;
12739
12823
  }
12740
12824
  }
@@ -12848,14 +12932,14 @@ function registerMemoryImport(memory2) {
12848
12932
 
12849
12933
  // src/commands/memory-import-changelog.ts
12850
12934
  import { existsSync as existsSync58 } from "fs";
12851
- import { readFile as readFile17, mkdir as mkdir15, writeFile as writeFile27 } from "fs/promises";
12935
+ import { readFile as readFile17, mkdir as mkdir14, writeFile as writeFile26 } from "fs/promises";
12852
12936
  import path37 from "path";
12853
12937
  import "commander";
12854
12938
  import {
12855
- buildFrontmatter as buildFrontmatter9,
12939
+ buildFrontmatter as buildFrontmatter8,
12856
12940
  findProjectRoot as findProjectRoot34,
12857
12941
  resolveHaivePaths as resolveHaivePaths31,
12858
- serializeMemory as serializeMemory25
12942
+ serializeMemory as serializeMemory24
12859
12943
  } from "@hivelore/core";
12860
12944
  function parseChangelog(content) {
12861
12945
  const entries = [];
@@ -12945,7 +13029,7 @@ function registerMemoryImportChangelog(memory2) {
12945
13029
  const pkgName = opts.package ?? path37.basename(path37.dirname(changelogPath));
12946
13030
  const scope = opts.scope ?? "team";
12947
13031
  const teamDir = path37.join(paths.memoriesDir, scope);
12948
- await mkdir15(teamDir, { recursive: true });
13032
+ await mkdir14(teamDir, { recursive: true });
12949
13033
  let saved = 0;
12950
13034
  for (const entry of entries) {
12951
13035
  const lines = [];
@@ -12971,7 +13055,7 @@ function registerMemoryImportChangelog(memory2) {
12971
13055
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
12972
13056
  );
12973
13057
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
12974
- const fm = buildFrontmatter9({
13058
+ const fm = buildFrontmatter8({
12975
13059
  type: "gotcha",
12976
13060
  slug,
12977
13061
  scope,
@@ -12985,9 +13069,9 @@ function registerMemoryImportChangelog(memory2) {
12985
13069
  paths: [path37.relative(root, changelogPath)],
12986
13070
  topic: `changelog-${pkgName}-${entry.version}`
12987
13071
  });
12988
- await writeFile27(
13072
+ await writeFile26(
12989
13073
  path37.join(teamDir, `${fm.id}.md`),
12990
- serializeMemory25({ frontmatter: fm, body: lines.join("\n") }),
13074
+ serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
12991
13075
  "utf8"
12992
13076
  );
12993
13077
  console.log(ui.green(` \u2713 ${fm.id}`));
@@ -13010,7 +13094,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
13010
13094
 
13011
13095
  // src/commands/memory-digest.ts
13012
13096
  import { existsSync as existsSync59 } from "fs";
13013
- import { writeFile as writeFile28 } from "fs/promises";
13097
+ import { writeFile as writeFile27 } from "fs/promises";
13014
13098
  import path38 from "path";
13015
13099
  import "commander";
13016
13100
  import {
@@ -13107,7 +13191,7 @@ function registerMemoryDigest(program2) {
13107
13191
  const digest = lines.join("\n");
13108
13192
  if (opts.out) {
13109
13193
  const outPath = path38.resolve(process.cwd(), opts.out);
13110
- await writeFile28(outPath, digest, "utf8");
13194
+ await writeFile27(outPath, digest, "utf8");
13111
13195
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
13112
13196
  } else {
13113
13197
  console.log(digest);
@@ -13116,22 +13200,22 @@ function registerMemoryDigest(program2) {
13116
13200
  }
13117
13201
 
13118
13202
  // src/commands/session-end.ts
13119
- import { writeFile as writeFile29, mkdir as mkdir16, readFile as readFile18, rm as rm2 } from "fs/promises";
13203
+ import { writeFile as writeFile28, mkdir as mkdir15, readFile as readFile18, rm as rm2 } from "fs/promises";
13120
13204
  import { existsSync as existsSync60 } from "fs";
13121
13205
  import { spawn as spawn4 } from "child_process";
13122
13206
  import path39 from "path";
13123
13207
  import { Option as Option2 } from "commander";
13124
13208
  import {
13125
- buildFrontmatter as buildFrontmatter10,
13209
+ buildFrontmatter as buildFrontmatter9,
13126
13210
  findProjectRoot as findProjectRoot36,
13127
13211
  loadConfig as loadConfig7,
13128
13212
  loadMemoriesFromDir as loadMemoriesFromDir31,
13129
13213
  loadPreventionEvents as loadPreventionEvents2,
13130
13214
  loadUsageIndex as loadUsageIndex26,
13131
- memoryFilePath as memoryFilePath10,
13215
+ memoryFilePath as memoryFilePath9,
13132
13216
  renderCaughtForYou,
13133
13217
  resolveHaivePaths as resolveHaivePaths33,
13134
- serializeMemory as serializeMemory26,
13218
+ serializeMemory as serializeMemory25,
13135
13219
  summarizeCaughtForYou,
13136
13220
  writeSessionHandoff as writeSessionHandoff2
13137
13221
  } from "@hivelore/core";
@@ -13443,7 +13527,7 @@ function registerSessionEnd(session2) {
13443
13527
  paths: filesTouched.length ? filesTouched : fm.anchor.paths
13444
13528
  }
13445
13529
  };
13446
- await writeFile29(topicMatch.filePath, serializeMemory26({ frontmatter: newFrontmatter, body }), "utf8");
13530
+ await writeFile28(topicMatch.filePath, serializeMemory25({ frontmatter: newFrontmatter, body }), "utf8");
13447
13531
  await cleanupObservations();
13448
13532
  if (!opts.quiet) {
13449
13533
  ui.success(`Session recap updated (revision #${revisionCount})`);
@@ -13454,7 +13538,7 @@ function registerSessionEnd(session2) {
13454
13538
  return;
13455
13539
  }
13456
13540
  }
13457
- const frontmatter = buildFrontmatter10({
13541
+ const frontmatter = buildFrontmatter9({
13458
13542
  type: "session_recap",
13459
13543
  slug: "recap",
13460
13544
  scope,
@@ -13464,9 +13548,9 @@ function registerSessionEnd(session2) {
13464
13548
  topic,
13465
13549
  status: "validated"
13466
13550
  });
13467
- const file = memoryFilePath10(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
13468
- await mkdir16(path39.dirname(file), { recursive: true });
13469
- await writeFile29(file, serializeMemory26({ frontmatter, body }), "utf8");
13551
+ const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
13552
+ await mkdir15(path39.dirname(file), { recursive: true });
13553
+ await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
13470
13554
  await cleanupObservations();
13471
13555
  if (!opts.quiet) {
13472
13556
  ui.success(`Session recap created`);
@@ -13657,7 +13741,7 @@ function detectFormat(filePath) {
13657
13741
 
13658
13742
  // src/commands/hub.ts
13659
13743
  import { existsSync as existsSync63 } from "fs";
13660
- import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile30, copyFile } from "fs/promises";
13744
+ import { mkdir as mkdir16, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
13661
13745
  import path41 from "path";
13662
13746
  import { spawnSync as spawnSync5 } from "child_process";
13663
13747
  import "commander";
@@ -13667,7 +13751,7 @@ import {
13667
13751
  loadMemoriesFromDir as loadMemoriesFromDir33,
13668
13752
  resolveHaivePaths as resolveHaivePaths35,
13669
13753
  saveConfig as saveConfig3,
13670
- serializeMemory as serializeMemory27
13754
+ serializeMemory as serializeMemory26
13671
13755
  } from "@hivelore/core";
13672
13756
  function registerHub(program2) {
13673
13757
  const hub = program2.command("hub").description(
@@ -13678,7 +13762,7 @@ function registerHub(program2) {
13678
13762
  "Initialize a new team-knowledge hub repo at <hubPath>.\n\n Creates a git repo with a .ai/ directory structure ready for shared memories.\n\n Example:\n hivelore hub init ../team-hub\n hivelore hub init /srv/git/team-knowledge\n"
13679
13763
  ).action(async (hubPath) => {
13680
13764
  const absPath = path41.resolve(hubPath);
13681
- await mkdir17(absPath, { recursive: true });
13765
+ await mkdir16(absPath, { recursive: true });
13682
13766
  const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
13683
13767
  if (gitCheck.status !== 0) {
13684
13768
  const init = spawnSync5("git", ["init"], { cwd: absPath, encoding: "utf8" });
@@ -13689,8 +13773,8 @@ function registerHub(program2) {
13689
13773
  }
13690
13774
  }
13691
13775
  const sharedDir = path41.join(absPath, ".ai", "memories", "shared");
13692
- await mkdir17(sharedDir, { recursive: true });
13693
- await writeFile30(
13776
+ await mkdir16(sharedDir, { recursive: true });
13777
+ await writeFile29(
13694
13778
  path41.join(absPath, ".ai", "README.md"),
13695
13779
  `# Hivelore Team Knowledge Hub
13696
13780
 
@@ -13712,7 +13796,7 @@ hivelore hub pull # import into a project
13712
13796
  `,
13713
13797
  "utf8"
13714
13798
  );
13715
- await writeFile30(
13799
+ await writeFile29(
13716
13800
  path41.join(absPath, ".gitignore"),
13717
13801
  ".ai/.cache/\n.ai/memories/personal/\n",
13718
13802
  "utf8"
@@ -13765,7 +13849,7 @@ Next steps:
13765
13849
  }
13766
13850
  const projectName = path41.basename(root);
13767
13851
  const destDir = path41.join(hubRoot, ".ai", "memories", "shared", projectName);
13768
- await mkdir17(destDir, { recursive: true });
13852
+ await mkdir16(destDir, { recursive: true });
13769
13853
  const all = await loadMemoriesFromDir33(paths.memoriesDir);
13770
13854
  const shared = all.filter(
13771
13855
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
@@ -13784,7 +13868,7 @@ Next steps:
13784
13868
  const fm = memory2.frontmatter;
13785
13869
  const fileName = `${fm.id}.md`;
13786
13870
  const destPath = path41.join(destDir, fileName);
13787
- await writeFile30(destPath, serializeMemory27(memory2), "utf8");
13871
+ await writeFile29(destPath, serializeMemory26(memory2), "utf8");
13788
13872
  pushed++;
13789
13873
  }
13790
13874
  console.log(ui.green(`\u2713 Pushed ${pushed} shared memor${pushed === 1 ? "y" : "ies"} to hub`));
@@ -13844,7 +13928,7 @@ Next steps:
13844
13928
  for (const sourceName of projectDirs) {
13845
13929
  const sourceDir = path41.join(hubSharedDir, sourceName);
13846
13930
  const destDir = path41.join(paths.memoriesDir, "shared", sourceName);
13847
- await mkdir17(destDir, { recursive: true });
13931
+ await mkdir16(destDir, { recursive: true });
13848
13932
  const sourceFiles = (await readdir9(sourceDir)).filter((f) => f.endsWith(".md"));
13849
13933
  const { loadMemoriesFromDir: loadDir } = await import("@hivelore/core");
13850
13934
  const existingInDest = await loadDir(destDir);
@@ -13905,7 +13989,7 @@ Next steps:
13905
13989
  console.log(ui.dim(" Run `hivelore hub push` to publish them to the hub."));
13906
13990
  }
13907
13991
  void readFile19;
13908
- void writeFile30;
13992
+ void writeFile29;
13909
13993
  void saveConfig3;
13910
13994
  });
13911
13995
  }
@@ -13913,7 +13997,7 @@ Next steps:
13913
13997
  // src/commands/stats.ts
13914
13998
  import "commander";
13915
13999
  import { existsSync as existsSync64 } from "fs";
13916
- import { mkdir as mkdir18, writeFile as writeFile31 } from "fs/promises";
14000
+ import { mkdir as mkdir17, writeFile as writeFile30 } from "fs/promises";
13917
14001
  import path43 from "path";
13918
14002
  import {
13919
14003
  aggregateUsage,
@@ -14020,7 +14104,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
14020
14104
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
14021
14105
  events = [];
14022
14106
  }
14023
- await mkdir18(path43.dirname(outAbs), { recursive: true });
14107
+ await mkdir17(path43.dirname(outAbs), { recursive: true });
14024
14108
  const payload = {
14025
14109
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
14026
14110
  project_root: root,
@@ -14032,7 +14116,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
14032
14116
  top_memory_reads: memoryHitsLeader,
14033
14117
  roi_hints: roiHints
14034
14118
  };
14035
- await writeFile31(outAbs, JSON.stringify(payload, null, 2), "utf8");
14119
+ await writeFile30(outAbs, JSON.stringify(payload, null, 2), "utf8");
14036
14120
  ui.success(`Wrote ROI / usage rollup \u2192 ${outAbs}`);
14037
14121
  }
14038
14122
  async function renderMemoryHits(paths, opts) {
@@ -14210,7 +14294,7 @@ function summarize(name, t0, payload, notes) {
14210
14294
 
14211
14295
  // src/commands/benchmark.ts
14212
14296
  import { existsSync as existsSync65 } from "fs";
14213
- import { readdir as readdir7, readFile as readFile20, writeFile as writeFile33 } from "fs/promises";
14297
+ import { readdir as readdir7, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
14214
14298
  import path44 from "path";
14215
14299
  import "commander";
14216
14300
  import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hivelore/core";
@@ -14227,7 +14311,7 @@ function registerBenchmark(program2) {
14227
14311
  const markdown = renderMarkdown(root, summary, rows);
14228
14312
  if (opts.out) {
14229
14313
  const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
14230
- await writeFile33(outFile, markdown, "utf8");
14314
+ await writeFile31(outFile, markdown, "utf8");
14231
14315
  ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
14232
14316
  return;
14233
14317
  }
@@ -14356,7 +14440,7 @@ function escapeRegExp(value) {
14356
14440
  }
14357
14441
 
14358
14442
  // src/commands/eval.ts
14359
- import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile34 } from "fs/promises";
14443
+ import { mkdir as mkdir18, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
14360
14444
  import { existsSync as existsSync66 } from "fs";
14361
14445
  import path45 from "path";
14362
14446
  import "commander";
@@ -14467,8 +14551,8 @@ function registerEval(program2) {
14467
14551
  report,
14468
14552
  gate_precision: gatePrecision
14469
14553
  };
14470
- await mkdir19(path45.dirname(baselineFile), { recursive: true });
14471
- await writeFile34(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
14554
+ await mkdir18(path45.dirname(baselineFile), { recursive: true });
14555
+ await writeFile33(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
14472
14556
  if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path45.relative(root, baselineFile)}`);
14473
14557
  }
14474
14558
  let delta = null;
@@ -14522,7 +14606,7 @@ function registerEval(program2) {
14522
14606
  const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision, authoredScore);
14523
14607
  if (opts.out) {
14524
14608
  const outFile = path45.isAbsolute(opts.out) ? opts.out : path45.join(root, opts.out);
14525
- await writeFile34(outFile, md, "utf8");
14609
+ await writeFile33(outFile, md, "utf8");
14526
14610
  ui.success(`wrote ${path45.relative(process.cwd(), outFile)}`);
14527
14611
  } else {
14528
14612
  console.log(md);
@@ -14739,21 +14823,21 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision, authoredScore
14739
14823
  }
14740
14824
 
14741
14825
  // src/commands/memory-suggest.ts
14742
- import { mkdir as mkdir20, writeFile as writeFile35 } from "fs/promises";
14826
+ import { mkdir as mkdir19, writeFile as writeFile34 } from "fs/promises";
14743
14827
  import { existsSync as existsSync67 } from "fs";
14744
14828
  import path46 from "path";
14745
14829
  import "commander";
14746
14830
  import {
14747
14831
  aggregateUsage as aggregateUsage2,
14748
- buildFrontmatter as buildFrontmatter11,
14832
+ buildFrontmatter as buildFrontmatter10,
14749
14833
  findProjectRoot as findProjectRoot43,
14750
14834
  loadConfig as loadConfig11,
14751
14835
  loadMemoriesFromDir as loadMemoriesFromDir35,
14752
- memoryFilePath as memoryFilePath11,
14836
+ memoryFilePath as memoryFilePath10,
14753
14837
  parseSince as parseSince2,
14754
14838
  readUsageEvents as readUsageEvents3,
14755
14839
  resolveHaivePaths as resolveHaivePaths39,
14756
- serializeMemory as serializeMemory28
14840
+ serializeMemory as serializeMemory27
14757
14841
  } from "@hivelore/core";
14758
14842
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
14759
14843
  "mem_search",
@@ -14831,7 +14915,7 @@ function registerMemorySuggest(memory2) {
14831
14915
  skipped.push({ query: s.query, reason: `similar memory already exists (${dup.memory.frontmatter.id})` });
14832
14916
  continue;
14833
14917
  }
14834
- const fm = buildFrontmatter11({
14918
+ const fm = buildFrontmatter10({
14835
14919
  type: s.inferred_type,
14836
14920
  slug,
14837
14921
  scope,
@@ -14841,13 +14925,13 @@ function registerMemorySuggest(memory2) {
14841
14925
  status
14842
14926
  });
14843
14927
  const body = renderTemplate(s, fm.id, status);
14844
- const file = memoryFilePath11(paths, fm.scope, fm.id, fm.module);
14845
- await mkdir20(path46.dirname(file), { recursive: true });
14928
+ const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
14929
+ await mkdir19(path46.dirname(file), { recursive: true });
14846
14930
  if (existsSync67(file)) {
14847
14931
  skipped.push({ query: s.query, reason: `file already exists at ${path46.relative(root, file)}` });
14848
14932
  continue;
14849
14933
  }
14850
- await writeFile35(file, serializeMemory28({ frontmatter: fm, body }), "utf8");
14934
+ await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
14851
14935
  created.push({ id: fm.id, file: path46.relative(root, file), query: s.query });
14852
14936
  }
14853
14937
  if (opts.json) {
@@ -14947,7 +15031,7 @@ function truncate2(text, max) {
14947
15031
 
14948
15032
  // src/commands/memory-archive.ts
14949
15033
  import { existsSync as existsSync68 } from "fs";
14950
- import { writeFile as writeFile36 } from "fs/promises";
15034
+ import { writeFile as writeFile35 } from "fs/promises";
14951
15035
  import path47 from "path";
14952
15036
  import "commander";
14953
15037
  import {
@@ -14958,7 +15042,7 @@ import {
14958
15042
  loadMemoriesFromDir as loadMemoriesFromDir36,
14959
15043
  loadUsageIndex as loadUsageIndex29,
14960
15044
  resolveHaivePaths as resolveHaivePaths40,
14961
- serializeMemory as serializeMemory29
15045
+ serializeMemory as serializeMemory28
14962
15046
  } from "@hivelore/core";
14963
15047
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
14964
15048
  function registerMemoryArchive(memory2) {
@@ -15041,7 +15125,7 @@ function registerMemoryArchive(memory2) {
15041
15125
  if (!found) continue;
15042
15126
  const fm = { ...found.memory.frontmatter, status: "deprecated" };
15043
15127
  try {
15044
- await writeFile36(c.filePath, serializeMemory29({ frontmatter: fm, body: found.memory.body }), "utf8");
15128
+ await writeFile35(c.filePath, serializeMemory28({ frontmatter: fm, body: found.memory.body }), "utf8");
15045
15129
  archived++;
15046
15130
  } catch (err) {
15047
15131
  if (!opts.json) {
@@ -15068,7 +15152,7 @@ function parseDays(input) {
15068
15152
 
15069
15153
  // src/commands/doctor.ts
15070
15154
  import { existsSync as existsSync69, statSync as statSync2 } from "fs";
15071
- import { readFile as readFile23, stat, writeFile as writeFile37 } from "fs/promises";
15155
+ import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
15072
15156
  import path48 from "path";
15073
15157
  import { execFileSync, execSync as execSync4 } from "child_process";
15074
15158
  import "commander";
@@ -15099,13 +15183,17 @@ function registerDoctor(program2) {
15099
15183
  const repairs = [];
15100
15184
  const config = await loadConfig13(paths);
15101
15185
  if (!existsSync69(paths.haiveDir)) {
15102
- findings.push({
15103
- severity: "error",
15104
- code: "not-initialized",
15105
- message: ".ai/ directory missing \u2014 Hivelore is not initialized in this project.",
15106
- fix: "hivelore init"
15107
- });
15108
- return emit(findings, opts);
15186
+ if (opts.json) {
15187
+ console.log(JSON.stringify({
15188
+ initialized: false,
15189
+ findings: [{ severity: "error", code: "not-initialized", message: ".ai/ directory missing \u2014 Hivelore is not initialized in this project.", fix: "hivelore init" }]
15190
+ }, null, 2));
15191
+ } else {
15192
+ ui.error(`Hivelore is not initialized at ${root} (no .ai/ directory).`);
15193
+ ui.info("Run `hivelore init` to set it up \u2014 everything it writes is removable (.ai/ + bridge files).");
15194
+ }
15195
+ process.exitCode = 1;
15196
+ return;
15109
15197
  }
15110
15198
  if (opts.fix && !opts.dryRun) {
15111
15199
  repairs.push(
@@ -15127,8 +15215,8 @@ function registerDoctor(program2) {
15127
15215
  fix: "hivelore init"
15128
15216
  });
15129
15217
  } else {
15130
- const { readFile: readFile29 } = await import("fs/promises");
15131
- const content = await readFile29(paths.projectContext, "utf8");
15218
+ const { readFile: readFile30 } = await import("fs/promises");
15219
+ const content = await readFile30(paths.projectContext, "utf8");
15132
15220
  const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `hivelore init`");
15133
15221
  if (isTemplate) {
15134
15222
  findings.push({
@@ -15378,8 +15466,8 @@ function registerDoctor(program2) {
15378
15466
  let hasClaudeEnforcement = false;
15379
15467
  if (existsSync69(claudeSettings)) {
15380
15468
  try {
15381
- const { readFile: readFile29 } = await import("fs/promises");
15382
- const raw = await readFile29(claudeSettings, "utf8");
15469
+ const { readFile: readFile30 } = await import("fs/promises");
15470
+ const raw = await readFile30(claudeSettings, "utf8");
15383
15471
  hasClaudeEnforcement = raw.includes("hivelore enforce session-start") && raw.includes("hivelore enforce pre-tool-use");
15384
15472
  } catch {
15385
15473
  hasClaudeEnforcement = false;
@@ -15402,7 +15490,7 @@ function registerDoctor(program2) {
15402
15490
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
15403
15491
  });
15404
15492
  }
15405
- findings.push(...await collectInstallFindings(root, "0.30.1"));
15493
+ findings.push(...await collectInstallFindings(root, "0.31.0"));
15406
15494
  findings.push(...await collectToolchainFindings(root));
15407
15495
  try {
15408
15496
  const legacyRaw = execSync4("haive-mcp --version", {
@@ -15410,7 +15498,7 @@ function registerDoctor(program2) {
15410
15498
  timeout: 3e3,
15411
15499
  stdio: ["ignore", "pipe", "ignore"]
15412
15500
  }).trim();
15413
- const cliVersion = "0.30.1";
15501
+ const cliVersion = "0.31.0";
15414
15502
  if (legacyRaw && legacyRaw !== cliVersion) {
15415
15503
  findings.push({
15416
15504
  severity: "warn",
@@ -15439,7 +15527,7 @@ npm uninstall -g @hivelore/mcp`
15439
15527
  staleConfigs.push(path48.relative(root, cfgPath));
15440
15528
  if (opts.fix && !opts.dryRun) {
15441
15529
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "hivelore"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
15442
- await writeFile37(cfgPath, updated, "utf8");
15530
+ await writeFile36(cfgPath, updated, "utf8");
15443
15531
  }
15444
15532
  }
15445
15533
  } catch {
@@ -16497,7 +16585,7 @@ function registerMemoryConflictCandidates(memory2) {
16497
16585
  // src/commands/enforce.ts
16498
16586
  import { execFile as execFile3, execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
16499
16587
  import { existsSync as existsSync76, statSync as statSync3 } from "fs";
16500
- import { chmod as chmod2, mkdir as mkdir21, readFile as readFile24, readdir as readdir8, rm as rm3, writeFile as writeFile38 } from "fs/promises";
16588
+ import { chmod as chmod2, mkdir as mkdir20, readFile as readFile24, readdir as readdir8, rm as rm3, writeFile as writeFile37 } from "fs/promises";
16501
16589
  import path54 from "path";
16502
16590
  import { promisify as promisify3 } from "util";
16503
16591
  import "commander";
@@ -16539,7 +16627,7 @@ function registerEnforce(program2) {
16539
16627
  enforce.command("install").description("Install Hivelore enforcement across MCP config, git hooks, CI template, and supported client hooks.").option("-d, --dir <dir>", "project root").option("--no-git", "skip git pre-commit/pre-push enforcement hooks").option("--no-claude", "skip Claude Code hooks").option("--no-ci", "skip GitHub Actions enforcement workflow").action(async (opts) => {
16540
16628
  const root = findProjectRoot52(opts.dir);
16541
16629
  const paths = resolveHaivePaths48(root);
16542
- await mkdir21(paths.haiveDir, { recursive: true });
16630
+ await mkdir20(paths.haiveDir, { recursive: true });
16543
16631
  const current = await loadConfig15(paths);
16544
16632
  await saveConfig4(paths, {
16545
16633
  ...current,
@@ -16607,8 +16695,18 @@ function registerEnforce(program2) {
16607
16695
  });
16608
16696
  enforce.command("finish").alias("completion").description(
16609
16697
  "Final agent-exit gate: verify the git sync/release protocol before reporting a task done."
16610
- ).option("-d, --dir <dir>", "project root").option("--explain", "group findings by blocking/review/info and show repair commands", false).option("--json", "emit JSON", false).action(async (opts) => {
16611
- const report = await buildFinishReport(opts.dir);
16698
+ ).option("-d, --dir <dir>", "project root").option("--explain", "group findings by blocking/review/info and show repair commands", false).option("--wait", "poll GitHub Actions until the runs for HEAD complete instead of failing on pending CI", false).option("--wait-timeout <minutes>", "max minutes to wait for CI with --wait", "15").option("--json", "emit JSON", false).action(async (opts) => {
16699
+ let report = await buildFinishReport(opts.dir);
16700
+ if (opts.wait) {
16701
+ const WAIT_CODES = /* @__PURE__ */ new Set(["github-actions-pending", "github-actions-runs-missing"]);
16702
+ const deadline = Date.now() + Math.max(1, Number(opts.waitTimeout ?? 15)) * 6e4;
16703
+ const onlyWaitingOnCi = (r) => r.should_block && r.findings.some((f) => f.severity === "error" && WAIT_CODES.has(f.code)) && !r.findings.some((f) => f.severity === "error" && !WAIT_CODES.has(f.code));
16704
+ while (onlyWaitingOnCi(report) && Date.now() < deadline) {
16705
+ if (!opts.json) ui.info("GitHub Actions still running for HEAD \u2014 rechecking in 20s (--wait)\u2026");
16706
+ await new Promise((resolve) => setTimeout(resolve, 2e4));
16707
+ report = await buildFinishReport(opts.dir);
16708
+ }
16709
+ }
16612
16710
  printReport(report, Boolean(opts.json), Boolean(opts.explain));
16613
16711
  if (report.should_block) {
16614
16712
  if (!opts.json) printNextRequiredAction(report);
@@ -16631,7 +16729,7 @@ function registerEnforce(program2) {
16631
16729
  if (!root) return;
16632
16730
  const paths = resolveHaivePaths48(root);
16633
16731
  if (!existsSync76(paths.haiveDir)) return;
16634
- await mkdir21(paths.runtimeDir, { recursive: true });
16732
+ await mkdir20(paths.runtimeDir, { recursive: true });
16635
16733
  const sessionId = opts.sessionId ?? payload.session_id;
16636
16734
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this Hivelore-initialized project.";
16637
16735
  await applyLightweightRepairs(root, paths);
@@ -17075,7 +17173,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
17075
17173
  memoryIds: briefing.memories.map((m) => m.id)
17076
17174
  });
17077
17175
  const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
17078
- await mkdir21(dir, { recursive: true });
17176
+ await mkdir20(dir, { recursive: true });
17079
17177
  const file = path54.join(dir, `${sessionId}.md`);
17080
17178
  const parts = [
17081
17179
  "# Hivelore Briefing",
@@ -17094,7 +17192,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
17094
17192
  if (briefing.setup_warnings.length > 0) {
17095
17193
  parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
17096
17194
  }
17097
- await writeFile38(file, parts.join("\n") + "\n", "utf8");
17195
+ await writeFile37(file, parts.join("\n") + "\n", "utf8");
17098
17196
  return file;
17099
17197
  }
17100
17198
  var PRODUCTION_CODE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|swift|rb|php|cs|cpp|cc|c|h|hpp|vue|svelte)$/i;
@@ -17180,7 +17278,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
17180
17278
  findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
17181
17279
  });
17182
17280
  }
17183
- findings.push(...await inspectIntegrationVersions(root, "0.30.1"));
17281
+ findings.push(...await inspectIntegrationVersions(root, "0.31.0"));
17184
17282
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
17185
17283
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
17186
17284
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
@@ -17428,14 +17526,39 @@ async function runPrecommitPolicy(paths, gate, stage) {
17428
17526
  const reviewWarnings = result.warnings.filter(
17429
17527
  (w) => w.level === "review" && !w.reasons.includes("sensor")
17430
17528
  );
17529
+ const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
17530
+ const reviewSeenFile = path54.join(paths.runtimeDir, "enforcement", "review-seen.json");
17531
+ let reviewSeen = {};
17532
+ try {
17533
+ reviewSeen = JSON.parse(await readFile24(reviewSeenFile, "utf8"));
17534
+ } catch {
17535
+ }
17536
+ const now = Date.now();
17537
+ const isFreshlySeen = (id) => {
17538
+ const at = Date.parse(reviewSeen[id] ?? "");
17539
+ return Number.isFinite(at) && now - at < REVIEW_SEEN_TTL_MS;
17540
+ };
17541
+ const newWarnings = reviewWarnings.filter((w) => !isFreshlySeen(w.id));
17542
+ const repeatCount = reviewWarnings.length - newWarnings.length;
17431
17543
  const reviewFinding = reviewWarnings.length > 0 ? [{
17432
17544
  severity: "warn",
17433
17545
  code: "anti-pattern-review",
17434
- message: `${reviewWarnings.length} documented lesson(s) plausibly match this diff \u2014 review before committing: ` + reviewWarnings.slice(0, 3).map((w) => `${w.id} (${w.reasons.join("+")})`).join(", ") + (reviewWarnings.length > 3 ? ", \u2026" : ""),
17546
+ message: `${reviewWarnings.length} documented lesson(s) plausibly match this diff \u2014 review before committing` + (newWarnings.length > 0 ? `: ${newWarnings.slice(0, 3).map((w) => `${w.id} (${w.reasons.join("+")})`).join(", ")}` + (newWarnings.length > 3 ? ", \u2026" : "") : "") + (repeatCount > 0 ? ` (+${repeatCount} shown in the last 24h \u2014 \`hivelore precommit\` lists all)` : "") + ".",
17435
17547
  fix: "Run `hivelore precommit` for the matched lines and repair commands; update the code, or retire the memory if it no longer applies.",
17436
17548
  memory_ids: reviewWarnings.slice(0, 10).map((w) => w.id),
17437
17549
  impact: 5
17438
17550
  }] : [];
17551
+ if (reviewWarnings.length > 0) {
17552
+ try {
17553
+ for (const w of reviewWarnings) reviewSeen[w.id] = new Date(now).toISOString();
17554
+ for (const [id, at] of Object.entries(reviewSeen)) {
17555
+ if (!Number.isFinite(Date.parse(at)) || now - Date.parse(at) >= REVIEW_SEEN_TTL_MS) delete reviewSeen[id];
17556
+ }
17557
+ await mkdir20(path54.dirname(reviewSeenFile), { recursive: true });
17558
+ await writeFile37(reviewSeenFile, JSON.stringify(reviewSeen, null, 2), "utf8");
17559
+ } catch {
17560
+ }
17561
+ }
17439
17562
  if (!result.should_block) {
17440
17563
  return [
17441
17564
  {
@@ -17568,7 +17691,7 @@ function isGeneratedArtifactStatusLine(line) {
17568
17691
  }
17569
17692
  async function cleanupRuntimeDir(runtimeDir) {
17570
17693
  let removed = 0;
17571
- await mkdir21(runtimeDir, { recursive: true });
17694
+ await mkdir20(runtimeDir, { recursive: true });
17572
17695
  const entries = await readdir8(runtimeDir, { withFileTypes: true }).catch(() => []);
17573
17696
  for (const entry of entries) {
17574
17697
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
@@ -17579,9 +17702,9 @@ async function cleanupRuntimeDir(runtimeDir) {
17579
17702
  await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
17580
17703
  removed++;
17581
17704
  }
17582
- await writeFile38(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
17705
+ await writeFile37(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
17583
17706
  if (!existsSync76(path54.join(runtimeDir, "README.md"))) {
17584
- await writeFile38(
17707
+ await writeFile37(
17585
17708
  path54.join(runtimeDir, "README.md"),
17586
17709
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. Hivelore cleanup preserves briefing markers so enforcement state remains valid.\n",
17587
17710
  "utf8"
@@ -17591,14 +17714,14 @@ async function cleanupRuntimeDir(runtimeDir) {
17591
17714
  }
17592
17715
  async function cleanupCacheDir(cacheDir) {
17593
17716
  let removed = 0;
17594
- await mkdir21(cacheDir, { recursive: true });
17717
+ await mkdir20(cacheDir, { recursive: true });
17595
17718
  const entries = await readdir8(cacheDir, { withFileTypes: true }).catch(() => []);
17596
17719
  for (const entry of entries) {
17597
17720
  if (entry.name === ".gitignore") continue;
17598
17721
  await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
17599
17722
  removed++;
17600
17723
  }
17601
- await writeFile38(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
17724
+ await writeFile37(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
17602
17725
  return removed;
17603
17726
  }
17604
17727
  async function cleanupEnforcementDir(enforcementDir) {
@@ -18069,7 +18192,7 @@ async function installGitEnforcement(root) {
18069
18192
  ui.warn("No .git directory found; git enforcement hooks skipped.");
18070
18193
  return;
18071
18194
  }
18072
- await mkdir21(hooksDir, { recursive: true });
18195
+ await mkdir20(hooksDir, { recursive: true });
18073
18196
  const resolveCli = `_hivelore() {
18074
18197
  if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
18075
18198
  elif command -v haive >/dev/null 2>&1; then haive "$@"
@@ -18107,14 +18230,14 @@ _hivelore enforce commit-msg "$1" --dir . || exit $?
18107
18230
  if (existsSync76(file)) {
18108
18231
  const current = await readFile24(file, "utf8").catch(() => "");
18109
18232
  if (current.includes(ENFORCE_HOOK_MARKER)) {
18110
- await writeFile38(file, hook.body, "utf8");
18233
+ await writeFile37(file, hook.body, "utf8");
18111
18234
  } else {
18112
- await writeFile38(file, `${current.trimEnd()}
18235
+ await writeFile37(file, `${current.trimEnd()}
18113
18236
 
18114
18237
  ${hook.body}`, "utf8");
18115
18238
  }
18116
18239
  } else {
18117
- await writeFile38(file, hook.body, "utf8");
18240
+ await writeFile37(file, hook.body, "utf8");
18118
18241
  }
18119
18242
  await chmod2(file, 493);
18120
18243
  }
@@ -18122,12 +18245,12 @@ ${hook.body}`, "utf8");
18122
18245
  }
18123
18246
  async function installCiEnforcement(root) {
18124
18247
  const workflowPath = path54.join(root, ".github", "workflows", "haive-enforcement.yml");
18125
- await mkdir21(path54.dirname(workflowPath), { recursive: true });
18248
+ await mkdir20(path54.dirname(workflowPath), { recursive: true });
18126
18249
  if (existsSync76(workflowPath)) {
18127
18250
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
18128
18251
  return;
18129
18252
  }
18130
- await writeFile38(workflowPath, `name: haive-enforcement
18253
+ await writeFile37(workflowPath, `name: haive-enforcement
18131
18254
 
18132
18255
  on:
18133
18256
  pull_request:
@@ -18333,6 +18456,115 @@ function runCommand4(cmd, args, cwd) {
18333
18456
  });
18334
18457
  }
18335
18458
 
18459
+ // src/commands/release.ts
18460
+ import { existsSync as existsSync77 } from "fs";
18461
+ import { readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
18462
+ import path55 from "path";
18463
+ import "commander";
18464
+ import { findProjectRoot as findProjectRoot53 } from "@hivelore/core";
18465
+ import { execFile as execFile4 } from "child_process";
18466
+ import { promisify as promisify4 } from "util";
18467
+ var exec2 = promisify4(execFile4);
18468
+ var VERSION_FILES2 = [
18469
+ "package.json",
18470
+ "packages/core/package.json",
18471
+ "packages/cli/package.json",
18472
+ "packages/mcp/package.json",
18473
+ "packages/embeddings/package.json"
18474
+ ];
18475
+ async function readCurrentVersion(root) {
18476
+ const pkg = JSON.parse(await readFile25(path55.join(root, "package.json"), "utf8"));
18477
+ if (!pkg.version) throw new Error("Root package.json has no version field.");
18478
+ return pkg.version;
18479
+ }
18480
+ function nextVersion(current, spec) {
18481
+ if (/^\d+\.\d+\.\d+$/.test(spec)) return spec;
18482
+ const m = current.match(/^(\d+)\.(\d+)\.(\d+)$/);
18483
+ if (!m) throw new Error(`Current version "${current}" is not X.Y.Z \u2014 pass an explicit version.`);
18484
+ const [major, minor, patch] = [Number(m[1]), Number(m[2]), Number(m[3])];
18485
+ if (spec === "patch") return `${major}.${minor}.${patch + 1}`;
18486
+ if (spec === "minor") return `${major}.${minor + 1}.0`;
18487
+ if (spec === "major") return `${major + 1}.0.0`;
18488
+ throw new Error(`Unknown bump "${spec}" \u2014 use patch | minor | major | X.Y.Z.`);
18489
+ }
18490
+ function registerRelease(program2) {
18491
+ const release = program2.command("release").description("Release protocol helpers: lockstep version bump + CHANGELOG scaffold, then tag + push.");
18492
+ release.command("bump <version>").description("Bump root + core/cli/mcp/embeddings in lockstep (patch|minor|major|X.Y.Z) and scaffold the CHANGELOG section.").option("--title <text>", "CHANGELOG section title (after the version)").option("-d, --dir <dir>", "project root").action(async (spec, opts) => {
18493
+ const root = findProjectRoot53(opts.dir);
18494
+ const current = await readCurrentVersion(root);
18495
+ const next = nextVersion(current, spec);
18496
+ for (const rel of VERSION_FILES2) {
18497
+ const file = path55.join(root, rel);
18498
+ if (!existsSync77(file)) {
18499
+ ui.warn(`skip ${rel} (missing)`);
18500
+ continue;
18501
+ }
18502
+ const raw = await readFile25(file, "utf8");
18503
+ const updated = raw.replace(`"version": "${current}"`, `"version": "${next}"`);
18504
+ if (updated === raw) {
18505
+ ui.error(`${rel} is not at ${current} \u2014 lockstep broken; fix versions manually first.`);
18506
+ process.exitCode = 1;
18507
+ return;
18508
+ }
18509
+ await writeFile38(file, updated, "utf8");
18510
+ }
18511
+ const changelog = path55.join(root, "CHANGELOG.md");
18512
+ if (existsSync77(changelog)) {
18513
+ const raw = await readFile25(changelog, "utf8");
18514
+ const heading = `## [${next}]${opts.title ? ` \u2014 ${opts.title}` : ""}`;
18515
+ if (!raw.includes(`## [${next}]`)) {
18516
+ await writeFile38(
18517
+ changelog,
18518
+ raw.replace("## [Unreleased]", `## [Unreleased]
18519
+
18520
+ ${heading}
18521
+
18522
+ - TODO: describe the changes.
18523
+ `),
18524
+ "utf8"
18525
+ );
18526
+ }
18527
+ }
18528
+ ui.success(`Bumped ${current} \u2192 ${next} across ${VERSION_FILES2.length} manifest(s); CHANGELOG scaffolded.`);
18529
+ ui.info("Next: fill the CHANGELOG entry, build/test, commit (the gate runs), then `hivelore release tag`.");
18530
+ });
18531
+ 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) => {
18532
+ const root = findProjectRoot53(opts.dir);
18533
+ const version = await readCurrentVersion(root);
18534
+ for (const rel of VERSION_FILES2.slice(1)) {
18535
+ const file = path55.join(root, rel);
18536
+ if (!existsSync77(file)) continue;
18537
+ const v = JSON.parse(await readFile25(file, "utf8")).version;
18538
+ if (v !== version) {
18539
+ ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
18540
+ process.exitCode = 1;
18541
+ return;
18542
+ }
18543
+ }
18544
+ const dirty = (await exec2("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
18545
+ if (dirty.length > 0) {
18546
+ ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
18547
+ process.exitCode = 1;
18548
+ return;
18549
+ }
18550
+ const tag = `v${version}`;
18551
+ const existing = (await exec2("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
18552
+ if (existing) {
18553
+ ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
18554
+ process.exitCode = 1;
18555
+ return;
18556
+ }
18557
+ await exec2("git", ["tag", tag], { cwd: root });
18558
+ ui.success(`Created ${tag} at HEAD.`);
18559
+ if (opts.push !== false) {
18560
+ await exec2("git", ["push"], { cwd: root });
18561
+ await exec2("git", ["push", "origin", tag], { cwd: root });
18562
+ ui.success(`Pushed branch and ${tag}.`);
18563
+ ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
18564
+ }
18565
+ });
18566
+ }
18567
+
18336
18568
  // src/commands/run.ts
18337
18569
  import "commander";
18338
18570
  function registerRun(program2) {
@@ -18346,15 +18578,15 @@ function registerRun(program2) {
18346
18578
  }
18347
18579
 
18348
18580
  // src/commands/sensors.ts
18349
- import { execFile as execFile4 } from "child_process";
18350
- import { existsSync as existsSync77 } from "fs";
18351
- import { chmod as chmod3, mkdir as mkdir23, readFile as readFile25, writeFile as writeFile39 } from "fs/promises";
18352
- import path55 from "path";
18353
- import { promisify as promisify4 } from "util";
18581
+ import { execFile as execFile5 } from "child_process";
18582
+ import { existsSync as existsSync78 } from "fs";
18583
+ import { chmod as chmod3, mkdir as mkdir21, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
18584
+ import path56 from "path";
18585
+ import { promisify as promisify5 } from "util";
18354
18586
  import "commander";
18355
18587
  import {
18356
18588
  extractSensorExamples as extractSensorExamples2,
18357
- findProjectRoot as findProjectRoot53,
18589
+ findProjectRoot as findProjectRoot54,
18358
18590
  isRetiredMemory as isRetiredMemory4,
18359
18591
  judgeProposedSensor as judgeProposedSensor2,
18360
18592
  loadConfig as loadConfig16,
@@ -18366,13 +18598,13 @@ import {
18366
18598
  sensorPatternBrittleness as sensorPatternBrittleness2,
18367
18599
  sensorSelfCheck as sensorSelfCheck2,
18368
18600
  scannableSensorTargets,
18369
- serializeMemory as serializeMemory30
18601
+ serializeMemory as serializeMemory29
18370
18602
  } from "@hivelore/core";
18371
- var exec2 = promisify4(execFile4);
18603
+ var exec3 = promisify5(execFile5);
18372
18604
  function registerSensors(program2) {
18373
18605
  const sensors = program2.command("sensors").description("Operate executable sensors derived from Hivelore memories");
18374
18606
  sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
18375
- const root = findProjectRoot53(opts.dir);
18607
+ const root = findProjectRoot54(opts.dir);
18376
18608
  const paths = resolveHaivePaths49(root);
18377
18609
  const rows = await sensorRows(paths);
18378
18610
  if (opts.json) {
@@ -18401,10 +18633,10 @@ function registerSensors(program2) {
18401
18633
  sensors.command("check").description(
18402
18634
  "Run regex sensors against a diff (the deterministic/computational layer); defaults to `git diff --cached`.\n Diff-scan layers: `sensors check` (regex) and `anti_patterns_check` (memory match) are components;\n `pre_commit_check` combines them; `hivelore enforce check` is THE gate that runs at commit."
18403
18635
  ).option("--diff-file <path>", "read unified diff from a file instead of staged changes").option("--json", "emit JSON", false).option("--commands", "ALSO execute shell/test sensors (runs repo-authored commands)", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
18404
- const root = findProjectRoot53(opts.dir);
18636
+ const root = findProjectRoot54(opts.dir);
18405
18637
  const paths = resolveHaivePaths49(root);
18406
18638
  const memories = await runnableSensorMemories(paths);
18407
- const diff = opts.diffFile ? await readFile25(path55.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
18639
+ const diff = opts.diffFile ? await readFile26(path56.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
18408
18640
  const targets = scannableSensorTargets(diff);
18409
18641
  const hits = runSensors3(memories, targets);
18410
18642
  const config = await loadConfig16(paths);
@@ -18479,9 +18711,9 @@ function registerSensors(program2) {
18479
18711
  process.exitCode = 1;
18480
18712
  return;
18481
18713
  }
18482
- const root = findProjectRoot53(opts.dir);
18714
+ const root = findProjectRoot54(opts.dir);
18483
18715
  const paths = resolveHaivePaths49(root);
18484
- const loaded = existsSync77(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
18716
+ const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
18485
18717
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
18486
18718
  if (!found) {
18487
18719
  ui.error(`No memory found with id ${id}`);
@@ -18531,7 +18763,7 @@ function registerSensors(program2) {
18531
18763
  },
18532
18764
  body: found.memory.body
18533
18765
  };
18534
- await writeFile39(found.filePath, serializeMemory30(next), "utf8");
18766
+ await writeFile39(found.filePath, serializeMemory29(next), "utf8");
18535
18767
  ui.success(`Updated ${id}: sensor severity=${severity}`);
18536
18768
  if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
18537
18769
  ui.info(`message=${sensor.message}`);
@@ -18548,9 +18780,9 @@ function registerSensors(program2) {
18548
18780
  process.exitCode = 1;
18549
18781
  return;
18550
18782
  }
18551
- const root = findProjectRoot53(opts.dir);
18783
+ const root = findProjectRoot54(opts.dir);
18552
18784
  const paths = resolveHaivePaths49(root);
18553
- const loaded = existsSync77(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
18785
+ const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
18554
18786
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
18555
18787
  if (!found) {
18556
18788
  ui.error(`No memory found with id ${id}`);
@@ -18587,7 +18819,7 @@ function registerSensors(program2) {
18587
18819
  process.exitCode = 1;
18588
18820
  return;
18589
18821
  }
18590
- await writeFile39(found.filePath, serializeMemory30({ frontmatter: { ...found.memory.frontmatter, sensor }, body: found.memory.body }), "utf8");
18822
+ await writeFile39(found.filePath, serializeMemory29({ frontmatter: { ...found.memory.frontmatter, sensor }, body: found.memory.body }), "utf8");
18591
18823
  ui.success(`Sensor accepted (${severity}) on ${id}`);
18592
18824
  ui.info(`pattern=${JSON.stringify(opts.pattern)}${opts.absent ? ` absent=${JSON.stringify(opts.absent)}` : ""}`);
18593
18825
  ui.info(
@@ -18601,16 +18833,16 @@ function registerSensors(program2) {
18601
18833
  process.exitCode = 1;
18602
18834
  return;
18603
18835
  }
18604
- const root = findProjectRoot53(opts.dir);
18836
+ const root = findProjectRoot54(opts.dir);
18605
18837
  const paths = resolveHaivePaths49(root);
18606
18838
  const rows = await sensorRows(paths);
18607
- const outDir = path55.resolve(root, opts.outDir ?? ".ai/generated");
18608
- await mkdir23(outDir, { recursive: true });
18609
- const outPath = path55.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
18839
+ const outDir = path56.resolve(root, opts.outDir ?? ".ai/generated");
18840
+ await mkdir21(outDir, { recursive: true });
18841
+ const outPath = path56.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
18610
18842
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
18611
18843
  await writeFile39(outPath, content, "utf8");
18612
18844
  if (format === "grep") await chmod3(outPath, 493);
18613
- ui.success(`Exported ${rows.length} sensor(s): ${path55.relative(root, outPath)}`);
18845
+ ui.success(`Exported ${rows.length} sensor(s): ${path56.relative(root, outPath)}`);
18614
18846
  });
18615
18847
  }
18616
18848
  function deriveProposedMessage(body, pattern, absent) {
@@ -18643,7 +18875,7 @@ async function sensorRows(paths) {
18643
18875
  });
18644
18876
  }
18645
18877
  async function runnableSensorMemories(paths, regexOnly = true) {
18646
- if (!existsSync77(paths.memoriesDir)) return [];
18878
+ if (!existsSync78(paths.memoriesDir)) return [];
18647
18879
  const loaded = await loadMemoriesFromDir41(paths.memoriesDir);
18648
18880
  return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
18649
18881
  const sensor = memory2.frontmatter.sensor;
@@ -18654,7 +18886,7 @@ async function runnableSensorMemories(paths, regexOnly = true) {
18654
18886
  }
18655
18887
  async function runCommandSensor(spec, root) {
18656
18888
  try {
18657
- await exec2("bash", ["-c", spec.command], { cwd: root, timeout: 12e4, maxBuffer: 8 * 1024 * 1024 });
18889
+ await exec3("bash", ["-c", spec.command], { cwd: root, timeout: 12e4, maxBuffer: 8 * 1024 * 1024 });
18658
18890
  return false;
18659
18891
  } catch {
18660
18892
  return true;
@@ -18662,7 +18894,7 @@ async function runCommandSensor(spec, root) {
18662
18894
  }
18663
18895
  async function stagedDiff(root) {
18664
18896
  try {
18665
- const { stdout } = await exec2("git", ["diff", "--cached"], { cwd: root });
18897
+ const { stdout } = await exec3("git", ["diff", "--cached"], { cwd: root });
18666
18898
  return stdout;
18667
18899
  } catch (err) {
18668
18900
  throw new Error(`git diff --cached failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -18693,19 +18925,19 @@ function shellQuote(value) {
18693
18925
  }
18694
18926
 
18695
18927
  // src/commands/ingest.ts
18696
- import { existsSync as existsSync78 } from "fs";
18697
- import { mkdir as mkdir24, readFile as readFile26, writeFile as writeFile40 } from "fs/promises";
18698
- import path56 from "path";
18928
+ import { existsSync as existsSync79 } from "fs";
18929
+ import { mkdir as mkdir23, readFile as readFile27, writeFile as writeFile40 } from "fs/promises";
18930
+ import path57 from "path";
18699
18931
  import "commander";
18700
18932
  import {
18701
18933
  draftsFromFindings as draftsFromFindings2,
18702
18934
  filterNewDrafts as filterNewDrafts2,
18703
- findProjectRoot as findProjectRoot54,
18935
+ findProjectRoot as findProjectRoot55,
18704
18936
  loadMemoriesFromDir as loadMemoriesFromDir43,
18705
- memoryFilePath as memoryFilePath12,
18937
+ memoryFilePath as memoryFilePath11,
18706
18938
  parseFindings as parseFindings2,
18707
18939
  resolveHaivePaths as resolveHaivePaths50,
18708
- serializeMemory as serializeMemory31
18940
+ serializeMemory as serializeMemory30
18709
18941
  } from "@hivelore/core";
18710
18942
  var SEVERITIES = ["info", "minor", "major", "critical", "blocker"];
18711
18943
  function registerIngest(program2) {
@@ -18729,9 +18961,9 @@ function registerIngest(program2) {
18729
18961
  process.exitCode = 1;
18730
18962
  return;
18731
18963
  }
18732
- const root = findProjectRoot54(opts.dir);
18964
+ const root = findProjectRoot55(opts.dir);
18733
18965
  const paths = resolveHaivePaths50(root);
18734
- if (!existsSync78(paths.haiveDir)) {
18966
+ if (!existsSync79(paths.haiveDir)) {
18735
18967
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
18736
18968
  process.exitCode = 1;
18737
18969
  return;
@@ -18752,14 +18984,14 @@ function registerIngest(program2) {
18752
18984
  process.exitCode = 1;
18753
18985
  return;
18754
18986
  }
18755
- const reportPath = path56.resolve(root, file);
18756
- if (!existsSync78(reportPath)) {
18987
+ const reportPath = path57.resolve(root, file);
18988
+ if (!existsSync79(reportPath)) {
18757
18989
  ui.error(`Report file not found: ${reportPath}`);
18758
18990
  process.exitCode = 1;
18759
18991
  return;
18760
18992
  }
18761
18993
  try {
18762
- raw = await readFile26(reportPath, "utf8");
18994
+ raw = await readFile27(reportPath, "utf8");
18763
18995
  } catch (err) {
18764
18996
  ui.error(`Could not read ${reportPath}: ${err instanceof Error ? err.message : String(err)}`);
18765
18997
  process.exitCode = 1;
@@ -18785,7 +19017,7 @@ function registerIngest(program2) {
18785
19017
  process.exitCode = 1;
18786
19018
  return;
18787
19019
  }
18788
- const existing = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir43(paths.memoriesDir) : [];
19020
+ const existing = existsSync79(paths.memoriesDir) ? await loadMemoriesFromDir43(paths.memoriesDir) : [];
18789
19021
  const existingTopics = new Set(
18790
19022
  existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
18791
19023
  );
@@ -18847,14 +19079,14 @@ function registerIngest(program2) {
18847
19079
  await writeDraft2(paths, draft);
18848
19080
  created++;
18849
19081
  }
18850
- ui.success(`Created ${created} proposed memory(ies) under ${path56.relative(root, paths.memoriesDir)}/`);
19082
+ ui.success(`Created ${created} proposed memory(ies) under ${path57.relative(root, paths.memoriesDir)}/`);
18851
19083
  ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
18852
19084
  });
18853
19085
  }
18854
19086
  async function writeDraft2(paths, draft) {
18855
- const file = memoryFilePath12(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
18856
- await mkdir24(path56.dirname(file), { recursive: true });
18857
- await writeFile40(file, serializeMemory31({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
19087
+ const file = memoryFilePath11(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
19088
+ await mkdir23(path57.dirname(file), { recursive: true });
19089
+ await writeFile40(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
18858
19090
  return file;
18859
19091
  }
18860
19092
  async function fetchSonarIssues(opts) {
@@ -18895,11 +19127,11 @@ async function fetchSonarIssues(opts) {
18895
19127
  }
18896
19128
 
18897
19129
  // src/commands/dashboard.ts
18898
- import { existsSync as existsSync79 } from "fs";
19130
+ import { existsSync as existsSync80 } from "fs";
18899
19131
  import "commander";
18900
19132
  import {
18901
19133
  buildDashboard,
18902
- findProjectRoot as findProjectRoot55,
19134
+ findProjectRoot as findProjectRoot56,
18903
19135
  loadConfig as loadConfig17,
18904
19136
  loadMemoriesFromDir as loadMemoriesFromDir44,
18905
19137
  loadPreventionEvents as loadPreventionEvents4,
@@ -18910,14 +19142,14 @@ function registerDashboard(program2) {
18910
19142
  program2.command("dashboard").description(
18911
19143
  "Non-interactive observability snapshot of the memory corpus.\n\n One-shot rollup an agent or CI can read (unlike `hivelore tui`, no TTY needed):\n inventory, impact tiers + top memories, sensors (and which ones fired),\n health (stale / anchorless / pending / prune candidates), decay, and corpus weight.\n Use --json to pipe it into other tooling."
18912
19144
  ).option("--json", "emit the full report as JSON", false).option("--top <n>", "rows per top-list", "10").option("--dormant-days <n>", "dormancy window for impact scoring").option("-d, --dir <dir>", "project root").action(async (opts) => {
18913
- const root = findProjectRoot55(opts.dir);
19145
+ const root = findProjectRoot56(opts.dir);
18914
19146
  const paths = resolveHaivePaths51(root);
18915
- if (!existsSync79(paths.haiveDir)) {
19147
+ if (!existsSync80(paths.haiveDir)) {
18916
19148
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
18917
19149
  process.exitCode = 1;
18918
19150
  return;
18919
19151
  }
18920
- const memories = existsSync79(paths.memoriesDir) ? await loadMemoriesFromDir44(paths.memoriesDir) : [];
19152
+ const memories = existsSync80(paths.memoriesDir) ? await loadMemoriesFromDir44(paths.memoriesDir) : [];
18921
19153
  const usage = await loadUsageIndex31(paths);
18922
19154
  const preventionEvents = await loadPreventionEvents4(paths);
18923
19155
  const config = await loadConfig17(paths);
@@ -19041,30 +19273,30 @@ function warnNum(n) {
19041
19273
  }
19042
19274
 
19043
19275
  // src/commands/dev-link.ts
19044
- import { execFile as execFile5 } from "child_process";
19045
- import { cp, readFile as readFile27 } from "fs/promises";
19046
- import { existsSync as existsSync80 } from "fs";
19047
- import path57 from "path";
19048
- import { promisify as promisify5 } from "util";
19276
+ import { execFile as execFile6 } from "child_process";
19277
+ import { cp, readFile as readFile28 } from "fs/promises";
19278
+ import { existsSync as existsSync81 } from "fs";
19279
+ import path58 from "path";
19280
+ import { promisify as promisify6 } from "util";
19049
19281
  import "commander";
19050
- import { findProjectRoot as findProjectRoot56 } from "@hivelore/core";
19051
- var exec3 = promisify5(execFile5);
19282
+ import { findProjectRoot as findProjectRoot57 } from "@hivelore/core";
19283
+ var exec4 = promisify6(execFile6);
19052
19284
  function registerDevLink(program2) {
19053
19285
  const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
19054
19286
  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) => {
19055
- const root = findProjectRoot56(opts.dir);
19056
- if (!existsSync80(path57.join(root, "packages", "cli", "dist", "index.js"))) {
19287
+ const root = findProjectRoot57(opts.dir);
19288
+ if (!existsSync81(path58.join(root, "packages", "cli", "dist", "index.js"))) {
19057
19289
  ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
19058
19290
  process.exitCode = 1;
19059
19291
  return;
19060
19292
  }
19061
19293
  let globalModules;
19062
19294
  try {
19063
- globalModules = (await exec3("npm", ["root", "-g"])).stdout.trim();
19295
+ globalModules = (await exec4("npm", ["root", "-g"])).stdout.trim();
19064
19296
  } catch {
19065
- globalModules = path57.join(path57.dirname(path57.dirname(process.execPath)), "lib", "node_modules");
19297
+ globalModules = path58.join(path58.dirname(path58.dirname(process.execPath)), "lib", "node_modules");
19066
19298
  }
19067
- const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path57.join(globalModules, scope)).filter((dir) => existsSync80(dir));
19299
+ const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path58.join(globalModules, scope)).filter((dir) => existsSync81(dir));
19068
19300
  if (scopeDirs.length === 0) {
19069
19301
  ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
19070
19302
  process.exitCode = 1;
@@ -19072,24 +19304,24 @@ function registerDevLink(program2) {
19072
19304
  }
19073
19305
  const linked = [];
19074
19306
  const copyDist = async (fromPkg, toDistDir) => {
19075
- const from = path57.join(root, "packages", fromPkg, "dist");
19076
- if (!existsSync80(from) || !existsSync80(path57.dirname(toDistDir))) return;
19307
+ const from = path58.join(root, "packages", fromPkg, "dist");
19308
+ if (!existsSync81(from) || !existsSync81(path58.dirname(toDistDir))) return;
19077
19309
  await cp(from, toDistDir, { recursive: true });
19078
- linked.push(path57.relative(globalModules, toDistDir));
19310
+ linked.push(path58.relative(globalModules, toDistDir));
19079
19311
  };
19080
19312
  for (const globalHive of scopeDirs) {
19081
- const nestedScope = path57.basename(globalHive);
19313
+ const nestedScope = path58.basename(globalHive);
19082
19314
  for (const pkg of ["cli", "mcp"]) {
19083
- await copyDist(pkg, path57.join(globalHive, pkg, "dist"));
19315
+ await copyDist(pkg, path58.join(globalHive, pkg, "dist"));
19084
19316
  for (const nested of ["core", "embeddings"]) {
19085
- await copyDist(nested, path57.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
19317
+ await copyDist(nested, path58.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
19086
19318
  }
19087
19319
  }
19088
- await copyDist("core", path57.join(globalHive, "core", "dist"));
19320
+ await copyDist("core", path58.join(globalHive, "core", "dist"));
19089
19321
  }
19090
19322
  let version = "unknown";
19091
19323
  try {
19092
- version = JSON.parse(await readFile27(path57.join(root, "package.json"), "utf8")).version ?? "unknown";
19324
+ version = JSON.parse(await readFile28(path58.join(root, "package.json"), "utf8")).version ?? "unknown";
19093
19325
  } catch {
19094
19326
  }
19095
19327
  if (opts.json) {
@@ -19100,27 +19332,27 @@ function registerDevLink(program2) {
19100
19332
  ui.warn("Nothing linked \u2014 no matching dist targets were found in the global install.");
19101
19333
  return;
19102
19334
  }
19103
- ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path57.basename(d)).join(", ")}`);
19335
+ ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path58.basename(d)).join(", ")}`);
19104
19336
  for (const t of linked) console.log(` ${ui.dim("\u2192")} ${t}`);
19105
19337
  console.log(ui.dim("The global binary now runs your local build (git hooks + MCP included)."));
19106
19338
  });
19107
19339
  }
19108
19340
 
19109
19341
  // src/commands/coverage.ts
19110
- import { readFile as readFile28 } from "fs/promises";
19111
- import { existsSync as existsSync81 } from "fs";
19112
- import path58 from "path";
19342
+ import { readFile as readFile29 } from "fs/promises";
19343
+ import { existsSync as existsSync83 } from "fs";
19344
+ import path59 from "path";
19113
19345
  import "commander";
19114
19346
  import {
19115
19347
  findCoverageGaps,
19116
- findProjectRoot as findProjectRoot57,
19348
+ findProjectRoot as findProjectRoot58,
19117
19349
  mergeHotFiles,
19118
19350
  resolveHaivePaths as resolveHaivePaths52,
19119
19351
  tallyHotFiles
19120
19352
  } from "@hivelore/core";
19121
19353
  async function readAgentHotFiles(root, cacheFile, sinceMs) {
19122
- if (!existsSync81(cacheFile)) return [];
19123
- const raw = await readFile28(cacheFile, "utf8").catch(() => "");
19354
+ if (!existsSync83(cacheFile)) return [];
19355
+ const raw = await readFile29(cacheFile, "utf8").catch(() => "");
19124
19356
  const files = [];
19125
19357
  for (const line of raw.split("\n")) {
19126
19358
  const trimmed = line.trim();
@@ -19133,7 +19365,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
19133
19365
  }
19134
19366
  for (const f of obs.files ?? []) {
19135
19367
  if (typeof f !== "string" || !f) continue;
19136
- const rel = path58.isAbsolute(f) ? path58.relative(root, f) : f;
19368
+ const rel = path59.isAbsolute(f) ? path59.relative(root, f) : f;
19137
19369
  if (rel.startsWith("..")) continue;
19138
19370
  files.push(rel);
19139
19371
  }
@@ -19154,7 +19386,7 @@ function registerCoverage(program2) {
19154
19386
  program2.command("coverage").description(
19155
19387
  "Coverage-gap report: frequently-edited files with no covering team memory (blind spots)."
19156
19388
  ).option("--json", "emit JSON", false).option("--min-changes <n>", "minimum churn count to flag a file", "3").option("--limit <n>", "max gaps to report", "20").option("--days <n>", "lookback window in days (git history + agent edits)", "90").option("--source <which>", "heat source: git | agent | both", "both").option("-d, --dir <dir>", "project root").action(async (opts) => {
19157
- const root = findProjectRoot57(opts.dir);
19389
+ const root = findProjectRoot58(opts.dir);
19158
19390
  const paths = resolveHaivePaths52(root);
19159
19391
  const minChanges = Math.max(1, parseInt(opts.minChanges ?? "3", 10) || 3);
19160
19392
  const limit = Math.max(1, parseInt(opts.limit ?? "20", 10) || 20);
@@ -19177,7 +19409,7 @@ function registerCoverage(program2) {
19177
19409
  }) : null;
19178
19410
  const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
19179
19411
  const sinceMs = Date.now() - days * 864e5;
19180
- const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path58.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
19412
+ const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path59.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
19181
19413
  const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
19182
19414
  const memories = await loadMemoriesFromDir29(paths.memoriesDir);
19183
19415
  const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
@@ -19215,10 +19447,10 @@ function registerCoverage(program2) {
19215
19447
 
19216
19448
  // src/commands/merge-driver.ts
19217
19449
  import { execFileSync as execFileSync3 } from "child_process";
19218
- import { readFileSync, writeFileSync, existsSync as existsSync83 } from "fs";
19219
- import path59 from "path";
19450
+ import { readFileSync, writeFileSync, existsSync as existsSync84 } from "fs";
19451
+ import path60 from "path";
19220
19452
  import "commander";
19221
- import { findProjectRoot as findProjectRoot58, mergeMemoryVersions } from "@hivelore/core";
19453
+ import { findProjectRoot as findProjectRoot59, mergeMemoryVersions } from "@hivelore/core";
19222
19454
  var GITATTRIBUTES_MARK = "# Hivelore merge driver";
19223
19455
  var GITATTRIBUTES_BLOCK = [
19224
19456
  GITATTRIBUTES_MARK,
@@ -19239,7 +19471,7 @@ function registerMergeDriver(program2) {
19239
19471
  }
19240
19472
  });
19241
19473
  cmd.command("install").description("Configure git + .gitattributes so memory-file conflicts auto-resolve").option("-d, --dir <dir>", "project root").action((opts) => {
19242
- const root = findProjectRoot58(opts.dir);
19474
+ const root = findProjectRoot59(opts.dir);
19243
19475
  try {
19244
19476
  execFileSync3("git", ["config", "merge.haive.name", "Hivelore memory merge driver"], { cwd: root });
19245
19477
  execFileSync3("git", ["config", "merge.haive.driver", "hivelore merge-driver run %O %A %B"], { cwd: root });
@@ -19248,8 +19480,8 @@ function registerMergeDriver(program2) {
19248
19480
  process.exitCode = 1;
19249
19481
  return;
19250
19482
  }
19251
- const gaPath = path59.join(root, ".gitattributes");
19252
- let content = existsSync83(gaPath) ? readFileSync(gaPath, "utf8") : "";
19483
+ const gaPath = path60.join(root, ".gitattributes");
19484
+ let content = existsSync84(gaPath) ? readFileSync(gaPath, "utf8") : "";
19253
19485
  if (!content.includes(GITATTRIBUTES_MARK)) {
19254
19486
  if (content.length > 0 && !content.endsWith("\n")) content += "\n";
19255
19487
  content += GITATTRIBUTES_BLOCK + "\n";
@@ -19264,20 +19496,20 @@ function registerMergeDriver(program2) {
19264
19496
 
19265
19497
  // src/commands/memory-resolve-conflict.ts
19266
19498
  import { writeFile as writeFile41 } from "fs/promises";
19267
- import { existsSync as existsSync84 } from "fs";
19499
+ import { existsSync as existsSync85 } from "fs";
19268
19500
  import "commander";
19269
19501
  import {
19270
19502
  applyConflictResolution,
19271
- findProjectRoot as findProjectRoot59,
19503
+ findProjectRoot as findProjectRoot60,
19272
19504
  planConflictResolution as planConflictResolution2,
19273
19505
  resolveHaivePaths as resolveHaivePaths53,
19274
- serializeMemory as serializeMemory33
19506
+ serializeMemory as serializeMemory31
19275
19507
  } from "@hivelore/core";
19276
19508
  function registerMemoryResolveConflict(memory2) {
19277
19509
  memory2.command("resolve-conflict <id_a> <id_b>").description("Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other").option("--yes", "apply the resolution (without this, only previews it)", false).option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (idA, idB, opts) => {
19278
- const root = findProjectRoot59(opts.dir);
19510
+ const root = findProjectRoot60(opts.dir);
19279
19511
  const paths = resolveHaivePaths53(root);
19280
- if (!existsSync84(paths.memoriesDir)) {
19512
+ if (!existsSync85(paths.memoriesDir)) {
19281
19513
  ui.error(`No .ai/memories at ${root}.`);
19282
19514
  process.exitCode = 1;
19283
19515
  return;
@@ -19317,12 +19549,12 @@ function registerMemoryResolveConflict(memory2) {
19317
19549
  }
19318
19550
  await writeFile41(
19319
19551
  winner.filePath,
19320
- serializeMemory33({ frontmatter: applied.winner, body: winner.memory.body }),
19552
+ serializeMemory31({ frontmatter: applied.winner, body: winner.memory.body }),
19321
19553
  "utf8"
19322
19554
  );
19323
19555
  await writeFile41(
19324
19556
  loser.filePath,
19325
- serializeMemory33({ frontmatter: applied.loser, body: loser.memory.body }),
19557
+ serializeMemory31({ frontmatter: applied.loser, body: loser.memory.body }),
19326
19558
  "utf8"
19327
19559
  );
19328
19560
  if (!opts.json) {
@@ -19332,26 +19564,26 @@ function registerMemoryResolveConflict(memory2) {
19332
19564
  }
19333
19565
 
19334
19566
  // src/commands/memory-seed-git.ts
19335
- import { execFile as execFile6 } from "child_process";
19336
- import { mkdir as mkdir25, writeFile as writeFile43 } from "fs/promises";
19337
- import { existsSync as existsSync85 } from "fs";
19338
- import path60 from "path";
19339
- import { promisify as promisify6 } from "util";
19567
+ import { execFile as execFile7 } from "child_process";
19568
+ import { mkdir as mkdir24, writeFile as writeFile43 } from "fs/promises";
19569
+ import { existsSync as existsSync86 } from "fs";
19570
+ import path61 from "path";
19571
+ import { promisify as promisify7 } from "util";
19340
19572
  import "commander";
19341
19573
  import {
19342
- buildFrontmatter as buildFrontmatter12,
19343
- findProjectRoot as findProjectRoot60,
19344
- memoryFilePath as memoryFilePath13,
19574
+ buildFrontmatter as buildFrontmatter11,
19575
+ findProjectRoot as findProjectRoot61,
19576
+ memoryFilePath as memoryFilePath12,
19345
19577
  proposeSeedsFromCommits as proposeSeedsFromCommits2,
19346
19578
  resolveHaivePaths as resolveHaivePaths54,
19347
- serializeMemory as serializeMemory34
19579
+ serializeMemory as serializeMemory33
19348
19580
  } from "@hivelore/core";
19349
- var exec4 = promisify6(execFile6);
19581
+ var exec5 = promisify7(execFile7);
19350
19582
  function registerMemorySeedGit(memory2) {
19351
19583
  memory2.command("seed-git").description("Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)").option("--apply", "write the proposed seeds as draft memories (default: preview only)", false).option("--limit <n>", "max seeds to propose", "20").option("--days <n>", "git-history lookback window in days", "365").option("--scope <scope>", "personal | team", "team").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
19352
- const root = findProjectRoot60(opts.dir);
19584
+ const root = findProjectRoot61(opts.dir);
19353
19585
  const paths = resolveHaivePaths54(root);
19354
- if (!existsSync85(paths.haiveDir)) {
19586
+ if (!existsSync86(paths.haiveDir)) {
19355
19587
  ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
19356
19588
  process.exitCode = 1;
19357
19589
  return;
@@ -19379,7 +19611,7 @@ function registerMemorySeedGit(memory2) {
19379
19611
  let written = 0;
19380
19612
  for (const p of proposals) {
19381
19613
  const fm = {
19382
- ...buildFrontmatter12({
19614
+ ...buildFrontmatter11({
19383
19615
  type: "attempt",
19384
19616
  slug: p.slug,
19385
19617
  scope: opts.scope ?? "team",
@@ -19395,10 +19627,10 @@ function registerMemorySeedGit(memory2) {
19395
19627
 
19396
19628
  _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
19397
19629
  `;
19398
- const file = memoryFilePath13(paths, fm.scope, fm.id, fm.module);
19399
- if (existsSync85(file)) continue;
19400
- await mkdir25(path60.dirname(file), { recursive: true });
19401
- await writeFile43(file, serializeMemory34({ frontmatter: fm, body }), "utf8");
19630
+ const file = memoryFilePath12(paths, fm.scope, fm.id, fm.module);
19631
+ if (existsSync86(file)) continue;
19632
+ await mkdir24(path61.dirname(file), { recursive: true });
19633
+ await writeFile43(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
19402
19634
  written += 1;
19403
19635
  }
19404
19636
  if (!opts.json) {
@@ -19408,7 +19640,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
19408
19640
  }
19409
19641
  async function readCommits(root, days) {
19410
19642
  try {
19411
- const { stdout } = await exec4(
19643
+ const { stdout } = await exec5(
19412
19644
  "git",
19413
19645
  ["log", `--since=${days}.days.ago`, "--name-only", "--pretty=format:%x1f%h%x1f%s", "-n", "500"],
19414
19646
  { cwd: root, maxBuffer: 8 * 1024 * 1024 }
@@ -19429,11 +19661,11 @@ async function readCommits(root, days) {
19429
19661
  }
19430
19662
 
19431
19663
  // src/commands/bridges.ts
19432
- import { existsSync as existsSync86 } from "fs";
19433
- import path61 from "path";
19664
+ import { existsSync as existsSync87 } from "fs";
19665
+ import path63 from "path";
19434
19666
  import "commander";
19435
19667
  import {
19436
- findProjectRoot as findProjectRoot61,
19668
+ findProjectRoot as findProjectRoot62,
19437
19669
  resolveHaivePaths as resolveHaivePaths55,
19438
19670
  BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
19439
19671
  BRIDGE_TARGETS as BRIDGE_TARGETS3
@@ -19448,10 +19680,10 @@ function registerBridges(program2) {
19448
19680
  "--only <targets>",
19449
19681
  "comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
19450
19682
  ).option("--max-memories <n>", "max memories to inject per bridge", "8").option("--dry-run", "show what would change without writing").option("-d, --dir <dir>", "project root").action(async (opts) => {
19451
- const root = findProjectRoot61(opts.dir);
19683
+ const root = findProjectRoot62(opts.dir);
19452
19684
  const paths = resolveHaivePaths55(root);
19453
19685
  const dryRun = opts.dryRun === true;
19454
- if (!existsSync86(paths.memoriesDir)) {
19686
+ if (!existsSync87(paths.memoriesDir)) {
19455
19687
  ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
19456
19688
  process.exitCode = 1;
19457
19689
  return;
@@ -19470,7 +19702,7 @@ function registerBridges(program2) {
19470
19702
  targets = BRIDGE_TARGETS3;
19471
19703
  } else {
19472
19704
  targets = BRIDGE_TARGETS3.filter(
19473
- (t) => existsSync86(path61.join(root, BRIDGE_TARGET_PATH3[t]))
19705
+ (t) => existsSync87(path63.join(root, BRIDGE_TARGET_PATH3[t]))
19474
19706
  );
19475
19707
  if (targets.length === 0) {
19476
19708
  ui.info(
@@ -19499,7 +19731,7 @@ function registerBridges(program2) {
19499
19731
  console.log(ui.dim(`bridges: ${parts.join(" \xB7 ") || "nothing to do"}`));
19500
19732
  });
19501
19733
  bridges.command("status").alias("list").description("List bridge targets and whether their Hivelore-managed blocks are current").option("-d, --dir <dir>", "project root").option("--max-memories <n>", "max memories expected in generated bridge blocks", "8").action(async (opts) => {
19502
- const root = findProjectRoot61(opts.dir);
19734
+ const root = findProjectRoot62(opts.dir);
19503
19735
  const paths = resolveHaivePaths55(root);
19504
19736
  const statuses = await getBridgeFileStatuses(root, paths, {
19505
19737
  targets: BRIDGE_TARGETS3,
@@ -19517,13 +19749,14 @@ function registerBridges(program2) {
19517
19749
  }
19518
19750
 
19519
19751
  // src/index.ts
19520
- var program = new Command64();
19521
- program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.30.1").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
19752
+ var program = new Command65();
19753
+ program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.31.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
19522
19754
  registerInit(program);
19523
19755
  registerWelcome(program);
19524
19756
  registerResolveProject(program);
19525
19757
  registerRuntime(program);
19526
19758
  registerEnforce(program);
19759
+ registerRelease(program);
19527
19760
  registerRun(program);
19528
19761
  registerAgent(program);
19529
19762
  registerSensors(program);