@hiveai/cli 0.10.9 → 0.11.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 Command53 } from "commander";
4
+ import { Command as Command55 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync as existsSync3 } from "fs";
@@ -199,7 +199,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
199
199
  if (!f) continue;
200
200
  counts.set(f, (counts.get(f) ?? 0) + 1);
201
201
  }
202
- let entries = [...counts.entries()].map(([path53, changes]) => ({ path: path53, changes }));
202
+ let entries = [...counts.entries()].map(([path54, changes]) => ({ path: path54, changes }));
203
203
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
204
204
  if (lowerPaths.length > 0) {
205
205
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -2743,7 +2743,7 @@ ${SEED_FOOTER(stack)}` });
2743
2743
  }
2744
2744
 
2745
2745
  // src/commands/init.ts
2746
- var HAIVE_GITHUB_ACTION_REF = `v${"0.10.9"}`;
2746
+ var HAIVE_GITHUB_ACTION_REF = `v${"0.11.0"}`;
2747
2747
  var PROJECT_CONTEXT_TEMPLATE = `# Project context
2748
2748
 
2749
2749
  > Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
@@ -3663,55 +3663,66 @@ import {
3663
3663
  serializeMemory as serializeMemory32
3664
3664
  } from "@hiveai/core";
3665
3665
  import { z as z7 } from "zod";
3666
- import { readFile as readFile22, readdir as readdir22 } from "fs/promises";
3667
- import { existsSync as existsSync82 } from "fs";
3668
- import path42 from "path";
3669
3666
  import {
3670
- deriveConfidence as deriveConfidence2,
3667
+ computeImpact,
3671
3668
  getUsage as getUsage22,
3672
- inferModulesFromPaths,
3673
3669
  loadMemoriesFromDir as loadMemoriesFromDir6,
3674
3670
  loadUsageIndex as loadUsageIndex32,
3675
- memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
3676
- trackReads as trackReads22
3671
+ recordApplied,
3672
+ recordRejection as recordRejection2,
3673
+ saveUsageIndex as saveUsageIndex2
3677
3674
  } from "@hiveai/core";
3675
+ import { existsSync as existsSync82 } from "fs";
3678
3676
  import { z as z8 } from "zod";
3677
+ import { readFile as readFile22, readdir as readdir22 } from "fs/promises";
3679
3678
  import { existsSync as existsSync92 } from "fs";
3679
+ import path42 from "path";
3680
3680
  import {
3681
- deriveConfidence as deriveConfidence3,
3681
+ deriveConfidence as deriveConfidence2,
3682
3682
  getUsage as getUsage3,
3683
+ inferModulesFromPaths,
3683
3684
  loadMemoriesFromDir as loadMemoriesFromDir7,
3684
- loadUsageIndex as loadUsageIndex4
3685
+ loadUsageIndex as loadUsageIndex4,
3686
+ memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
3687
+ trackReads as trackReads22
3685
3688
  } from "@hiveai/core";
3686
3689
  import { z as z9 } from "zod";
3687
3690
  import { existsSync as existsSync102 } from "fs";
3688
- import { unlink } from "fs/promises";
3689
3691
  import {
3692
+ deriveConfidence as deriveConfidence3,
3693
+ getUsage as getUsage4,
3690
3694
  loadMemoriesFromDir as loadMemoriesFromDir8,
3691
- loadUsageIndex as loadUsageIndex5,
3692
- saveUsageIndex as saveUsageIndex2
3695
+ loadUsageIndex as loadUsageIndex5
3693
3696
  } from "@hiveai/core";
3694
3697
  import { z as z10 } from "zod";
3695
- import { writeFile as writeFile52 } from "fs/promises";
3696
3698
  import { existsSync as existsSync112 } from "fs";
3697
- import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
3698
- import { z as z11 } from "zod";
3699
- import { existsSync as existsSync122 } from "fs";
3699
+ import { unlink } from "fs/promises";
3700
3700
  import {
3701
- getUsage as getUsage4,
3702
- loadMemoriesFromDir as loadMemoriesFromDir10,
3703
- loadUsageIndex as loadUsageIndex6
3701
+ loadMemoriesFromDir as loadMemoriesFromDir9,
3702
+ loadUsageIndex as loadUsageIndex6,
3703
+ saveUsageIndex as saveUsageIndex3
3704
3704
  } from "@hiveai/core";
3705
+ import { z as z11 } from "zod";
3706
+ import { writeFile as writeFile52 } from "fs/promises";
3707
+ import { existsSync as existsSync122 } from "fs";
3708
+ import { loadMemoriesFromDir as loadMemoriesFromDir10, serializeMemory as serializeMemory4 } from "@hiveai/core";
3705
3709
  import { z as z12 } from "zod";
3706
- import { writeFile as writeFile62 } from "fs/promises";
3707
3710
  import { existsSync as existsSync132 } from "fs";
3708
3711
  import {
3712
+ getUsage as getUsage5,
3709
3713
  loadMemoriesFromDir as loadMemoriesFromDir11,
3710
- serializeMemory as serializeMemory5
3714
+ loadUsageIndex as loadUsageIndex7
3711
3715
  } from "@hiveai/core";
3712
3716
  import { z as z13 } from "zod";
3713
- import { mkdir as mkdir32, writeFile as writeFile72 } from "fs/promises";
3717
+ import { writeFile as writeFile62 } from "fs/promises";
3714
3718
  import { existsSync as existsSync142 } from "fs";
3719
+ import {
3720
+ loadMemoriesFromDir as loadMemoriesFromDir12,
3721
+ serializeMemory as serializeMemory5
3722
+ } from "@hiveai/core";
3723
+ import { z as z14 } from "zod";
3724
+ import { mkdir as mkdir32, writeFile as writeFile72 } from "fs/promises";
3725
+ import { existsSync as existsSync15 } from "fs";
3715
3726
  import path52 from "path";
3716
3727
  import {
3717
3728
  buildFrontmatter as buildFrontmatter22,
@@ -3719,9 +3730,9 @@ import {
3719
3730
  serializeMemory as serializeMemory6,
3720
3731
  suggestSensorFromMemory as suggestSensorFromMemory2
3721
3732
  } from "@hiveai/core";
3722
- import { z as z14 } from "zod";
3733
+ import { z as z15 } from "zod";
3723
3734
  import { mkdir as mkdir42, writeFile as writeFile82 } from "fs/promises";
3724
- import { existsSync as existsSync15 } from "fs";
3735
+ import { existsSync as existsSync16 } from "fs";
3725
3736
  import path62 from "path";
3726
3737
  import {
3727
3738
  buildFrontmatter as buildFrontmatter3,
@@ -3729,35 +3740,37 @@ import {
3729
3740
  memoryFilePath as memoryFilePath3,
3730
3741
  serializeMemory as serializeMemory7
3731
3742
  } from "@hiveai/core";
3732
- import { z as z15 } from "zod";
3743
+ import { z as z16 } from "zod";
3733
3744
  import { writeFile as writeFile10, mkdir as mkdir62 } from "fs/promises";
3734
- import { existsSync as existsSync17 } from "fs";
3745
+ import { existsSync as existsSync18 } from "fs";
3735
3746
  import path82 from "path";
3736
3747
  import {
3737
3748
  buildFrontmatter as buildFrontmatter4,
3738
- loadMemoriesFromDir as loadMemoriesFromDir12,
3749
+ loadMemoriesFromDir as loadMemoriesFromDir13,
3739
3750
  memoryFilePath as memoryFilePath4,
3740
3751
  serializeMemory as serializeMemory8
3741
3752
  } from "@hiveai/core";
3742
- import { z as z16 } from "zod";
3753
+ import { z as z17 } from "zod";
3743
3754
  import {
3744
3755
  appendUsageEvent,
3745
3756
  appendRuntimeJournalEntry,
3746
3757
  loadConfig as loadConfig22
3747
3758
  } from "@hiveai/core";
3748
3759
  import { mkdir as mkdir52, writeFile as writeFile92, rm } from "fs/promises";
3749
- import { existsSync as existsSync16 } from "fs";
3760
+ import { existsSync as existsSync17 } from "fs";
3750
3761
  import path72 from "path";
3751
3762
  import { execSync } from "child_process";
3752
3763
  import { readFile as readFile42, writeFile as writeFile11 } from "fs/promises";
3753
- import { existsSync as existsSync19 } from "fs";
3764
+ import { existsSync as existsSync20 } from "fs";
3754
3765
  import {
3755
3766
  allocateBudget,
3767
+ computeImpact as computeImpact2,
3756
3768
  DEFAULT_AUTO_PROMOTE_RULE,
3757
3769
  deriveConfidence as deriveConfidence4,
3758
3770
  estimateTokens,
3771
+ evaluateSkillActivation,
3759
3772
  extractActionsBriefBody as extractActionsBriefBody2,
3760
- getUsage as getUsage5,
3773
+ getUsage as getUsage6,
3761
3774
  inferModulesFromPaths as inferModulesFromPaths2,
3762
3775
  isAutoPromoteEligible,
3763
3776
  isDecaying,
@@ -3766,8 +3779,8 @@ import {
3766
3779
  literalMatchesAnyToken as literalMatchesAnyToken22,
3767
3780
  loadCodeMap as loadCodeMap5,
3768
3781
  loadConfig as loadConfig3,
3769
- loadMemoriesFromDir as loadMemoriesFromDir13,
3770
- loadUsageIndex as loadUsageIndex7,
3782
+ loadMemoriesFromDir as loadMemoriesFromDir14,
3783
+ loadUsageIndex as loadUsageIndex8,
3771
3784
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths22,
3772
3785
  queryCodeMap as queryCodeMap2,
3773
3786
  resolveBriefingBudget as resolveBriefingBudget2,
@@ -3779,77 +3792,77 @@ import {
3779
3792
  truncateToTokens,
3780
3793
  writeBriefingMarker as writeBriefingMarker2
3781
3794
  } from "@hiveai/core";
3782
- import { z as z17 } from "zod";
3795
+ import { z as z18 } from "zod";
3783
3796
  import { readdir as readdir3, readFile as readFile32 } from "fs/promises";
3784
- import { existsSync as existsSync18 } from "fs";
3797
+ import { existsSync as existsSync19 } from "fs";
3785
3798
  import path92 from "path";
3786
3799
  import { isGlobPath, isStackPackSeed as isStackPackSeed2, pathsOverlap } from "@hiveai/core";
3787
3800
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
3788
- import { z as z18 } from "zod";
3789
- import { existsSync as existsSync20 } from "fs";
3790
- import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
3791
3801
  import { z as z19 } from "zod";
3792
3802
  import { existsSync as existsSync21 } from "fs";
3793
3803
  import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
3794
3804
  import { z as z20 } from "zod";
3805
+ import { existsSync as existsSync222 } from "fs";
3806
+ import { loadMemoriesFromDir as loadMemoriesFromDir16 } from "@hiveai/core";
3795
3807
  import { z as z21 } from "zod";
3796
3808
  import { z as z22 } from "zod";
3797
- import { existsSync as existsSync222 } from "fs";
3809
+ import { z as z23 } from "zod";
3810
+ import { existsSync as existsSync23 } from "fs";
3798
3811
  import { spawn } from "child_process";
3799
3812
  import path102 from "path";
3800
3813
  import {
3801
3814
  deriveConfidence as deriveConfidence5,
3802
- getUsage as getUsage6,
3815
+ getUsage as getUsage7,
3803
3816
  loadCodeMap as loadCodeMap32,
3804
- loadMemoriesFromDir as loadMemoriesFromDir16,
3805
- loadUsageIndex as loadUsageIndex8,
3817
+ loadMemoriesFromDir as loadMemoriesFromDir17,
3818
+ loadUsageIndex as loadUsageIndex9,
3806
3819
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
3807
3820
  } from "@hiveai/core";
3808
- import { z as z23 } from "zod";
3809
- import { existsSync as existsSync23 } from "fs";
3821
+ import { z as z24 } from "zod";
3822
+ import { existsSync as existsSync24 } from "fs";
3810
3823
  import {
3811
3824
  addedLinesFromDiff,
3812
3825
  deriveConfidence as deriveConfidence6,
3813
- getUsage as getUsage7,
3826
+ getUsage as getUsage8,
3814
3827
  isRetiredMemory as isRetiredMemory2,
3815
- loadMemoriesFromDir as loadMemoriesFromDir17,
3816
- loadUsageIndex as loadUsageIndex9,
3828
+ loadMemoriesFromDir as loadMemoriesFromDir18,
3829
+ loadUsageIndex as loadUsageIndex10,
3817
3830
  literalMatchesAnyToken as literalMatchesAnyToken3,
3818
3831
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths4,
3819
3832
  runSensors,
3820
3833
  sensorTargetsFromDiff,
3821
3834
  tokenizeQuery as tokenizeQuery3
3822
3835
  } from "@hiveai/core";
3823
- import { z as z24 } from "zod";
3824
- import { existsSync as existsSync24 } from "fs";
3825
- import {
3826
- loadMemoriesFromDir as loadMemoriesFromDir18,
3827
- tokenizeQuery as tokenizeQuery4
3828
- } from "@hiveai/core";
3829
3836
  import { z as z25 } from "zod";
3830
3837
  import { existsSync as existsSync25 } from "fs";
3831
- import { spawn as spawn2 } from "child_process";
3832
3838
  import {
3833
- deriveConfidence as deriveConfidence7,
3834
- getUsage as getUsage8,
3835
3839
  loadMemoriesFromDir as loadMemoriesFromDir19,
3836
- loadUsageIndex as loadUsageIndex10,
3837
- pathsOverlap as singlePathsOverlap
3840
+ tokenizeQuery as tokenizeQuery4
3838
3841
  } from "@hiveai/core";
3839
3842
  import { z as z26 } from "zod";
3840
3843
  import { existsSync as existsSync26 } from "fs";
3844
+ import { spawn as spawn2 } from "child_process";
3841
3845
  import {
3842
- deriveConfidence as deriveConfidence8,
3846
+ deriveConfidence as deriveConfidence7,
3843
3847
  getUsage as getUsage9,
3844
3848
  loadMemoriesFromDir as loadMemoriesFromDir20,
3845
3849
  loadUsageIndex as loadUsageIndex11,
3850
+ pathsOverlap as singlePathsOverlap
3851
+ } from "@hiveai/core";
3852
+ import { z as z27 } from "zod";
3853
+ import { existsSync as existsSync27 } from "fs";
3854
+ import {
3855
+ deriveConfidence as deriveConfidence8,
3856
+ getUsage as getUsage10,
3857
+ loadMemoriesFromDir as loadMemoriesFromDir21,
3858
+ loadUsageIndex as loadUsageIndex12,
3846
3859
  pathsOverlap as pathsOverlap2,
3847
3860
  tokenizeQuery as tokenizeQuery5
3848
3861
  } from "@hiveai/core";
3849
- import { z as z27 } from "zod";
3850
3862
  import { z as z28 } from "zod";
3863
+ import { z as z29 } from "zod";
3851
3864
  import { mkdir as mkdir72, writeFile as writeFile12 } from "fs/promises";
3852
- import { existsSync as existsSync27 } from "fs";
3865
+ import { existsSync as existsSync28 } from "fs";
3853
3866
  import path112 from "path";
3854
3867
  import { execSync as execSync2 } from "child_process";
3855
3868
  import {
@@ -3858,28 +3871,28 @@ import {
3858
3871
  readUsageEvents,
3859
3872
  serializeMemory as serializeMemory10
3860
3873
  } from "@hiveai/core";
3861
- import { z as z29 } from "zod";
3862
- import { existsSync as existsSync28 } from "fs";
3874
+ import { z as z30 } from "zod";
3875
+ import { existsSync as existsSync29 } from "fs";
3863
3876
  import {
3864
3877
  findLexicalConflictPairs,
3865
3878
  findTopicStatusConflictPairs,
3866
- loadMemoriesFromDir as loadMemoriesFromDir21
3879
+ loadMemoriesFromDir as loadMemoriesFromDir222
3867
3880
  } from "@hiveai/core";
3868
- import { z as z30 } from "zod";
3869
- import { resolveProjectInfo } from "@hiveai/core";
3870
3881
  import { z as z31 } from "zod";
3871
- import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3882
+ import { resolveProjectInfo } from "@hiveai/core";
3872
3883
  import { z as z32 } from "zod";
3873
- import { existsSync as existsSync29 } from "fs";
3874
- import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir222 } from "@hiveai/core";
3884
+ import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3875
3885
  import { z as z33 } from "zod";
3876
- import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
3886
+ import { existsSync as existsSync30 } from "fs";
3887
+ import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hiveai/core";
3877
3888
  import { z as z34 } from "zod";
3878
- import { readRuntimeJournalTail } from "@hiveai/core";
3889
+ import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
3879
3890
  import { z as z35 } from "zod";
3891
+ import { readRuntimeJournalTail } from "@hiveai/core";
3880
3892
  import { z as z36 } from "zod";
3881
3893
  import { z as z37 } from "zod";
3882
3894
  import { z as z38 } from "zod";
3895
+ import { z as z39 } from "zod";
3883
3896
  import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
3884
3897
  function createContext(options = {}) {
3885
3898
  const env = options.env ?? process.env;
@@ -3996,6 +4009,13 @@ var MemSaveInputSchema = {
3996
4009
  commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)"),
3997
4010
  topic: z4.string().optional().describe(
3998
4011
  "Stable key for this memory. If a memory with the same topic already exists in this scope, it is updated in-place (revision_count++). Use for knowledge that evolves over time."
4012
+ ),
4013
+ activation: z4.object({
4014
+ keywords: z4.array(z4.string()).default([]),
4015
+ globs: z4.array(z4.string()).default([]),
4016
+ always: z4.boolean().default(false)
4017
+ }).optional().describe(
4018
+ "Only for type='skill'. Progressive-disclosure triggers: the skill is surfaced ONLY when a keyword matches the task or a glob matches the edited files (or always=true). Omit to keep the skill always-eligible."
3999
4019
  )
4000
4020
  };
4001
4021
  function bodyHash(body) {
@@ -4114,7 +4134,8 @@ async function memSave(input, ctx) {
4114
4134
  commit: input.commit,
4115
4135
  topic: input.topic,
4116
4136
  status: haiveConfig.defaultStatus === "validated" ? "validated" : void 0,
4117
- sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0
4137
+ sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0,
4138
+ activation: input.type === "skill" ? input.activation : void 0
4118
4139
  });
4119
4140
  const file = memoryFilePath2(
4120
4141
  ctx.paths,
@@ -4463,14 +4484,51 @@ async function memReject(input, ctx) {
4463
4484
  rejection_reason: u?.rejection_reason ?? null
4464
4485
  };
4465
4486
  }
4487
+ var MemFeedbackInputSchema = {
4488
+ id: z8.string().min(1).describe("Full memory id the feedback is about"),
4489
+ outcome: z8.enum(["applied", "rejected"]).describe(
4490
+ "'applied' = this memory changed what you did (strong positive utility signal); 'rejected' = it was wrong/outdated/unhelpful (negative signal, blocks auto-promotion)."
4491
+ ),
4492
+ reason: z8.string().optional().describe("Why it was rejected (stored on the memory's usage record). Only used for outcome='rejected'.")
4493
+ };
4494
+ async function memFeedback(input, ctx) {
4495
+ if (!existsSync82(ctx.paths.memoriesDir)) {
4496
+ return { ok: false, id: input.id, error: "No .ai/memories \u2014 run `haive init` first." };
4497
+ }
4498
+ const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
4499
+ const target = all.find((m) => m.memory.frontmatter.id === input.id);
4500
+ if (!target) {
4501
+ return { ok: false, id: input.id, error: `No memory with id '${input.id}'.` };
4502
+ }
4503
+ const index = await loadUsageIndex32(ctx.paths);
4504
+ if (input.outcome === "applied") {
4505
+ recordApplied(index, input.id);
4506
+ } else {
4507
+ recordRejection2(index, input.id, input.reason ?? null);
4508
+ }
4509
+ await saveUsageIndex2(ctx.paths, index);
4510
+ const usage = getUsage22(index, input.id);
4511
+ const impact = computeImpact(target.memory.frontmatter, usage);
4512
+ return {
4513
+ ok: true,
4514
+ id: input.id,
4515
+ outcome: input.outcome,
4516
+ usage: {
4517
+ read_count: usage.read_count,
4518
+ applied_count: usage.applied_count,
4519
+ rejected_count: usage.rejected_count
4520
+ },
4521
+ impact: { score: impact.score, tier: impact.tier, signals: impact.signals }
4522
+ };
4523
+ }
4466
4524
  var MemForFilesInputSchema = {
4467
- files: z8.array(z8.string()).min(1).describe("Project-relative file paths the agent is currently working on"),
4468
- include_module_contexts: z8.boolean().default(true).describe("Inline the matching .ai/modules/<name>/context.md contents"),
4469
- track: z8.boolean().default(true).describe("Increment read_count on returned memories")
4525
+ files: z9.array(z9.string()).min(1).describe("Project-relative file paths the agent is currently working on"),
4526
+ include_module_contexts: z9.boolean().default(true).describe("Inline the matching .ai/modules/<name>/context.md contents"),
4527
+ track: z9.boolean().default(true).describe("Increment read_count on returned memories")
4470
4528
  };
4471
4529
  async function memForFiles(input, ctx) {
4472
4530
  const inferred = inferModulesFromPaths(input.files);
4473
- if (!existsSync82(ctx.paths.memoriesDir)) {
4531
+ if (!existsSync92(ctx.paths.memoriesDir)) {
4474
4532
  return {
4475
4533
  inferred_modules: inferred,
4476
4534
  by_anchor: [],
@@ -4479,8 +4537,8 @@ async function memForFiles(input, ctx) {
4479
4537
  module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
4480
4538
  };
4481
4539
  }
4482
- const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
4483
- const usage = await loadUsageIndex32(ctx.paths);
4540
+ const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
4541
+ const usage = await loadUsageIndex4(ctx.paths);
4484
4542
  const seen = /* @__PURE__ */ new Set();
4485
4543
  const byAnchor = [];
4486
4544
  const byModule = [];
@@ -4528,7 +4586,7 @@ async function memForFiles(input, ctx) {
4528
4586
  }
4529
4587
  function toMatch(loaded, reason, usage) {
4530
4588
  const fm = loaded.memory.frontmatter;
4531
- const u = getUsage22(usage, fm.id);
4589
+ const u = getUsage3(usage, fm.id);
4532
4590
  return {
4533
4591
  id: fm.id,
4534
4592
  scope: fm.scope,
@@ -4592,7 +4650,7 @@ function extractPathSegments(files) {
4592
4650
  }
4593
4651
  async function loadModuleContexts(ctx, modules, enabled) {
4594
4652
  if (!enabled || modules.length === 0) return [];
4595
- if (!existsSync82(ctx.paths.modulesContextDir)) return [];
4653
+ if (!existsSync92(ctx.paths.modulesContextDir)) return [];
4596
4654
  const available = new Set(
4597
4655
  (await readdir22(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
4598
4656
  );
@@ -4600,24 +4658,24 @@ async function loadModuleContexts(ctx, modules, enabled) {
4600
4658
  for (const m of modules) {
4601
4659
  if (!available.has(m)) continue;
4602
4660
  const file = path42.join(ctx.paths.modulesContextDir, m, "context.md");
4603
- if (existsSync82(file)) {
4661
+ if (existsSync92(file)) {
4604
4662
  out.push({ name: m, content: await readFile22(file, "utf8") });
4605
4663
  }
4606
4664
  }
4607
4665
  return out;
4608
4666
  }
4609
4667
  var MemGetInputSchema = {
4610
- id: z9.string().min(1).describe("Memory id to fetch")
4668
+ id: z10.string().min(1).describe("Memory id to fetch")
4611
4669
  };
4612
4670
  async function memGet(input, ctx) {
4613
- if (!existsSync92(ctx.paths.memoriesDir)) {
4671
+ if (!existsSync102(ctx.paths.memoriesDir)) {
4614
4672
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
4615
4673
  }
4616
- const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
4674
+ const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
4617
4675
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
4618
4676
  if (!found) throw new Error(`No memory with id "${input.id}".`);
4619
4677
  const fm = found.memory.frontmatter;
4620
- const u = getUsage3(await loadUsageIndex4(ctx.paths), fm.id);
4678
+ const u = getUsage4(await loadUsageIndex5(ctx.paths), fm.id);
4621
4679
  return {
4622
4680
  id: fm.id,
4623
4681
  scope: fm.scope,
@@ -4641,43 +4699,43 @@ async function memGet(input, ctx) {
4641
4699
  };
4642
4700
  }
4643
4701
  var MemDeleteInputSchema = {
4644
- id: z10.string().min(1).describe("Memory id to delete"),
4645
- keep_usage: z10.boolean().default(false).describe("Keep the usage.json entry instead of removing it alongside the file")
4702
+ id: z11.string().min(1).describe("Memory id to delete"),
4703
+ keep_usage: z11.boolean().default(false).describe("Keep the usage.json entry instead of removing it alongside the file")
4646
4704
  };
4647
4705
  async function memDelete(input, ctx) {
4648
- if (!existsSync102(ctx.paths.memoriesDir)) {
4706
+ if (!existsSync112(ctx.paths.memoriesDir)) {
4649
4707
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
4650
4708
  }
4651
- const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
4709
+ const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
4652
4710
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
4653
4711
  if (!found) throw new Error(`No memory with id "${input.id}".`);
4654
4712
  await unlink(found.filePath);
4655
4713
  let usageRemoved = false;
4656
4714
  if (!input.keep_usage) {
4657
- const idx = await loadUsageIndex5(ctx.paths);
4715
+ const idx = await loadUsageIndex6(ctx.paths);
4658
4716
  if (idx.by_id[input.id]) {
4659
4717
  delete idx.by_id[input.id];
4660
- await saveUsageIndex2(ctx.paths, idx);
4718
+ await saveUsageIndex3(ctx.paths, idx);
4661
4719
  usageRemoved = true;
4662
4720
  }
4663
4721
  }
4664
4722
  return { id: input.id, deleted_file: found.filePath, usage_removed: usageRemoved };
4665
4723
  }
4666
4724
  var MemUpdateInputSchema = {
4667
- id: z11.string().min(1).describe("Id of the memory to update"),
4668
- body: z11.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
4669
- tags: z11.array(z11.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
4670
- paths: z11.array(z11.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
4671
- symbols: z11.array(z11.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
4672
- commit: z11.string().optional().describe("New anchor commit SHA"),
4673
- domain: z11.string().optional().describe("New domain label"),
4674
- author: z11.string().optional().describe("New author handle or email")
4725
+ id: z12.string().min(1).describe("Id of the memory to update"),
4726
+ body: z12.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
4727
+ tags: z12.array(z12.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
4728
+ paths: z12.array(z12.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
4729
+ symbols: z12.array(z12.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
4730
+ commit: z12.string().optional().describe("New anchor commit SHA"),
4731
+ domain: z12.string().optional().describe("New domain label"),
4732
+ author: z12.string().optional().describe("New author handle or email")
4675
4733
  };
4676
4734
  async function memUpdate(input, ctx) {
4677
- if (!existsSync112(ctx.paths.memoriesDir)) {
4735
+ if (!existsSync122(ctx.paths.memoriesDir)) {
4678
4736
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
4679
4737
  }
4680
- const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
4738
+ const memories = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
4681
4739
  const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
4682
4740
  if (!loaded) throw new Error(`No memory with id "${input.id}".`);
4683
4741
  const { frontmatter, body } = loaded.memory;
@@ -4718,12 +4776,12 @@ async function memUpdate(input, ctx) {
4718
4776
  return { id: input.id, file_path: loaded.filePath, updated_fields };
4719
4777
  }
4720
4778
  var MemPendingInputSchema = {
4721
- scope: z12.enum(["personal", "team", "module"]).optional()
4779
+ scope: z13.enum(["personal", "team", "module"]).optional()
4722
4780
  };
4723
4781
  async function memPending(input, ctx) {
4724
- if (!existsSync122(ctx.paths.memoriesDir)) return { pending: [] };
4725
- const all = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
4726
- const usage = await loadUsageIndex6(ctx.paths);
4782
+ if (!existsSync132(ctx.paths.memoriesDir)) return { pending: [] };
4783
+ const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
4784
+ const usage = await loadUsageIndex7(ctx.paths);
4727
4785
  const now = Date.now();
4728
4786
  const proposed = all.filter(({ memory: memory2 }) => {
4729
4787
  if (memory2.frontmatter.status !== "proposed") return false;
@@ -4731,12 +4789,12 @@ async function memPending(input, ctx) {
4731
4789
  return true;
4732
4790
  });
4733
4791
  proposed.sort(
4734
- (a, b) => getUsage4(usage, b.memory.frontmatter.id).read_count - getUsage4(usage, a.memory.frontmatter.id).read_count
4792
+ (a, b) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
4735
4793
  );
4736
4794
  return {
4737
4795
  pending: proposed.map(({ memory: memory2, filePath }) => {
4738
4796
  const fm = memory2.frontmatter;
4739
- const u = getUsage4(usage, fm.id);
4797
+ const u = getUsage5(usage, fm.id);
4740
4798
  return {
4741
4799
  id: fm.id,
4742
4800
  scope: fm.scope,
@@ -4752,13 +4810,13 @@ async function memPending(input, ctx) {
4752
4810
  };
4753
4811
  }
4754
4812
  var MemApproveInputSchema = {
4755
- id: z13.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
4813
+ id: z14.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
4756
4814
  };
4757
4815
  async function memApprove(input, ctx) {
4758
- if (!existsSync132(ctx.paths.memoriesDir)) {
4816
+ if (!existsSync142(ctx.paths.memoriesDir)) {
4759
4817
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
4760
4818
  }
4761
- const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
4819
+ const all = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
4762
4820
  const found = all.find((m) => m.memory.frontmatter.id === input.id);
4763
4821
  if (!found) throw new Error(`No memory with id "${input.id}".`);
4764
4822
  const previous = found.memory.frontmatter.status;
@@ -4775,17 +4833,17 @@ async function memApprove(input, ctx) {
4775
4833
  };
4776
4834
  }
4777
4835
  var MemTriedInputSchema = {
4778
- what: z14.string().min(1).describe("Brief description of the approach that was tried"),
4779
- why_failed: z14.string().min(1).describe("Why it failed or why it should NOT be used"),
4780
- instead: z14.string().optional().describe("What to use or do instead (recommended alternative)"),
4781
- scope: z14.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
4782
- module: z14.string().optional().describe("Module name (required when scope=module)"),
4783
- tags: z14.array(z14.string()).default([]).describe("Tags for filtering"),
4784
- paths: z14.array(z14.string()).default([]).describe("Anchor file paths this applies to"),
4785
- author: z14.string().optional().describe("Author handle or email")
4836
+ what: z15.string().min(1).describe("Brief description of the approach that was tried"),
4837
+ why_failed: z15.string().min(1).describe("Why it failed or why it should NOT be used"),
4838
+ instead: z15.string().optional().describe("What to use or do instead (recommended alternative)"),
4839
+ scope: z15.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
4840
+ module: z15.string().optional().describe("Module name (required when scope=module)"),
4841
+ tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
4842
+ paths: z15.array(z15.string()).default([]).describe("Anchor file paths this applies to"),
4843
+ author: z15.string().optional().describe("Author handle or email")
4786
4844
  };
4787
4845
  async function memTried(input, ctx) {
4788
- if (!existsSync142(ctx.paths.haiveDir)) {
4846
+ if (!existsSync15(ctx.paths.haiveDir)) {
4789
4847
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
4790
4848
  }
4791
4849
  const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
@@ -4811,27 +4869,27 @@ async function memTried(input, ctx) {
4811
4869
  }
4812
4870
  const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
4813
4871
  await mkdir32(path52.dirname(file), { recursive: true });
4814
- if (existsSync142(file)) {
4872
+ if (existsSync15(file)) {
4815
4873
  throw new Error(`Memory already exists at ${file}`);
4816
4874
  }
4817
4875
  await writeFile72(file, serializeMemory6({ frontmatter, body }), "utf8");
4818
4876
  return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
4819
4877
  }
4820
4878
  var MemObserveInputSchema = {
4821
- what: z15.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
4822
- where: z15.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
4823
- impact: z15.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
4824
- fix: z15.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
4825
- scope: z15.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
4826
- module: z15.string().optional().describe("Module name (required when scope=module)"),
4827
- tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
4828
- author: z15.string().optional().describe("Author handle or email"),
4829
- force: z15.boolean().default(false).describe(
4879
+ what: z16.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
4880
+ where: z16.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
4881
+ impact: z16.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
4882
+ fix: z16.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
4883
+ scope: z16.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
4884
+ module: z16.string().optional().describe("Module name (required when scope=module)"),
4885
+ tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
4886
+ author: z16.string().optional().describe("Author handle or email"),
4887
+ force: z16.boolean().default(false).describe(
4830
4888
  "Save even if the observation looks like generic, guessable knowledge. By default, low-specificity observations (things a capable model already knows) are SKIPPED to keep the corpus high-signal \u2014 only unguessable, team-specific discoveries are worth storing."
4831
4889
  )
4832
4890
  };
4833
4891
  async function memObserve(input, ctx) {
4834
- if (!existsSync15(ctx.paths.haiveDir)) {
4892
+ if (!existsSync16(ctx.paths.haiveDir)) {
4835
4893
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
4836
4894
  }
4837
4895
  const signalText = [input.what, input.impact, input.fix ?? ""].join(" ");
@@ -4865,7 +4923,7 @@ async function memObserve(input, ctx) {
4865
4923
  const body = lines.join("\n") + "\n";
4866
4924
  const file = memoryFilePath3(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
4867
4925
  await mkdir42(path62.dirname(file), { recursive: true });
4868
- if (existsSync15(file)) {
4926
+ if (existsSync16(file)) {
4869
4927
  throw new Error(`Memory already exists at ${file}`);
4870
4928
  }
4871
4929
  await writeFile82(file, serializeMemory7({ frontmatter, body }), "utf8");
@@ -4951,7 +5009,7 @@ var SessionTracker = class {
4951
5009
  (e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
4952
5010
  );
4953
5011
  const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
4954
- if (!ranPostTask && isSubstantialSession && existsSync16(this.ctx.paths.haiveDir)) {
5012
+ if (!ranPostTask && isSubstantialSession && existsSync17(this.ctx.paths.haiveDir)) {
4955
5013
  try {
4956
5014
  const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
4957
5015
  const payload = {
@@ -4985,7 +5043,7 @@ var SessionTracker = class {
4985
5043
  };
4986
5044
  async function clearPendingDistill(ctx) {
4987
5045
  const p = pendingDistillPath(ctx);
4988
- if (existsSync16(p)) {
5046
+ if (existsSync17(p)) {
4989
5047
  try {
4990
5048
  await rm(p);
4991
5049
  } catch {
@@ -5000,15 +5058,15 @@ function summarizeTools(events) {
5000
5058
  return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} \xD7${n}`).join(", ");
5001
5059
  }
5002
5060
  var MemSessionEndInputSchema = {
5003
- goal: z16.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
5004
- accomplished: z16.string().describe("What was actually done \u2014 bullet list recommended"),
5005
- discoveries: z16.string().default("").describe(
5061
+ goal: z17.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
5062
+ accomplished: z17.string().describe("What was actually done \u2014 bullet list recommended"),
5063
+ discoveries: z17.string().default("").describe(
5006
5064
  "Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
5007
5065
  ),
5008
- files_touched: z16.array(z16.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
5009
- next_steps: z16.string().default("").describe("What should happen next (for the next session or a teammate)"),
5010
- scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
5011
- module: z16.string().optional().describe("Module name (required when scope=module)")
5066
+ files_touched: z17.array(z17.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
5067
+ next_steps: z17.string().default("").describe("What should happen next (for the next session or a teammate)"),
5068
+ scope: z17.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
5069
+ module: z17.string().optional().describe("Module name (required when scope=module)")
5012
5070
  };
5013
5071
  function recapTopic(scope, module) {
5014
5072
  return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
@@ -5038,7 +5096,7 @@ ${input.next_steps}`);
5038
5096
  return lines.join("\n");
5039
5097
  }
5040
5098
  async function memSessionEnd(input, ctx) {
5041
- if (!existsSync17(ctx.paths.haiveDir)) {
5099
+ if (!existsSync18(ctx.paths.haiveDir)) {
5042
5100
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
5043
5101
  }
5044
5102
  const body = buildBody(input);
@@ -5049,12 +5107,12 @@ async function memSessionEnd(input, ctx) {
5049
5107
  return rel.startsWith("..") ? p : rel;
5050
5108
  });
5051
5109
  const invalidPaths = normalizedFiles.filter(
5052
- (p) => !existsSync17(path82.resolve(ctx.paths.root, p))
5110
+ (p) => !existsSync18(path82.resolve(ctx.paths.root, p))
5053
5111
  );
5054
5112
  if (invalidPaths.length > 0) {
5055
5113
  console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
5056
5114
  }
5057
- const existing = existsSync17(ctx.paths.memoriesDir) ? await loadMemoriesFromDir12(ctx.paths.memoriesDir) : [];
5115
+ const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir13(ctx.paths.memoriesDir) : [];
5058
5116
  const topicMatch = existing.find(
5059
5117
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === input.scope && (!input.module || memory2.frontmatter.module === input.module)
5060
5118
  );
@@ -5235,7 +5293,7 @@ async function trySemanticHits(ctx, task, limit) {
5235
5293
  }
5236
5294
  async function loadModuleContexts2(ctx, modules) {
5237
5295
  if (modules.length === 0) return [];
5238
- if (!existsSync18(ctx.paths.modulesContextDir)) return [];
5296
+ if (!existsSync19(ctx.paths.modulesContextDir)) return [];
5239
5297
  const available = new Set(
5240
5298
  (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
5241
5299
  );
@@ -5243,42 +5301,42 @@ async function loadModuleContexts2(ctx, modules) {
5243
5301
  for (const m of modules) {
5244
5302
  if (!available.has(m)) continue;
5245
5303
  const file = path92.join(ctx.paths.modulesContextDir, m, "context.md");
5246
- if (existsSync18(file)) {
5304
+ if (existsSync19(file)) {
5247
5305
  out.push({ name: m, content: await readFile32(file, "utf8") });
5248
5306
  }
5249
5307
  }
5250
5308
  return out;
5251
5309
  }
5252
5310
  var GetBriefingInputSchema = {
5253
- task: z17.string().optional().describe(
5311
+ task: z18.string().optional().describe(
5254
5312
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
5255
5313
  ),
5256
- files: z17.array(z17.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
5257
- max_tokens: z17.number().int().positive().default(8e3).describe(
5314
+ files: z18.array(z18.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
5315
+ max_tokens: z18.number().int().positive().default(8e3).describe(
5258
5316
  "Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
5259
5317
  ),
5260
- max_memories: z17.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
5261
- include_project_context: z17.boolean().default(true),
5262
- include_module_contexts: z17.boolean().default(true),
5263
- semantic: z17.boolean().default(true).describe(
5318
+ max_memories: z18.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
5319
+ include_project_context: z18.boolean().default(true),
5320
+ include_module_contexts: z18.boolean().default(true),
5321
+ semantic: z18.boolean().default(true).describe(
5264
5322
  "Use semantic ranking when a task is provided (requires `haive embeddings index`)."
5265
5323
  ),
5266
- include_stale: z17.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
5267
- track: z17.boolean().default(true).describe("Increment read_count on returned memories"),
5268
- format: z17.enum(["full", "compact", "actions"]).default("full").describe(
5324
+ include_stale: z18.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
5325
+ track: z18.boolean().default(true).describe("Increment read_count on returned memories"),
5326
+ format: z18.enum(["full", "compact", "actions"]).default("full").describe(
5269
5327
  "Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
5270
5328
  ),
5271
- symbols: z17.array(z17.string()).default([]).describe(
5329
+ symbols: z18.array(z18.string()).default([]).describe(
5272
5330
  "Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `haive index code` to have been run."
5273
5331
  ),
5274
- min_semantic_score: z17.number().min(0).max(1).default(0).describe(
5332
+ min_semantic_score: z18.number().min(0).max(1).default(0).describe(
5275
5333
  "Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
5276
5334
  ),
5277
- budget_preset: z17.enum(["quick", "balanced", "deep"]).optional().describe(
5335
+ budget_preset: z18.enum(["quick", "balanced", "deep"]).optional().describe(
5278
5336
  "Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
5279
5337
  )
5280
5338
  };
5281
- var GetBriefingZod = z17.object(GetBriefingInputSchema);
5339
+ var GetBriefingZod = z18.object(GetBriefingInputSchema);
5282
5340
  async function getBriefing(input, ctx) {
5283
5341
  const resolvedBudget = resolveBriefingBudget2(input.budget_preset, {
5284
5342
  max_tokens: input.max_tokens,
@@ -5294,8 +5352,8 @@ async function getBriefing(input, ctx) {
5294
5352
  let usage = { version: 1, updated_at: "", by_id: {} };
5295
5353
  let byId = /* @__PURE__ */ new Map();
5296
5354
  let lastSession;
5297
- if (existsSync19(ctx.paths.memoriesDir)) {
5298
- const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
5355
+ if (existsSync20(ctx.paths.memoriesDir)) {
5356
+ const allLoaded = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
5299
5357
  const recaps = allLoaded.filter(({ memory: memory2 }) => memory2.frontmatter.type === "session_recap").sort(
5300
5358
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
5301
5359
  );
@@ -5317,7 +5375,7 @@ async function getBriefing(input, ctx) {
5317
5375
  if (memory2.frontmatter.type === "session_recap") return false;
5318
5376
  return true;
5319
5377
  });
5320
- usage = await loadUsageIndex7(ctx.paths);
5378
+ usage = await loadUsageIndex8(ctx.paths);
5321
5379
  byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
5322
5380
  const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
5323
5381
  if (input.task && input.semantic) {
@@ -5339,7 +5397,8 @@ async function getBriefing(input, ctx) {
5339
5397
  }
5340
5398
  return;
5341
5399
  }
5342
- const u = getUsage5(usage, fm.id);
5400
+ const u = getUsage6(usage, fm.id);
5401
+ const imp = computeImpact2(fm, u);
5343
5402
  seen.set(fm.id, {
5344
5403
  id: fm.id,
5345
5404
  scope: fm.scope,
@@ -5350,6 +5409,8 @@ async function getBriefing(input, ctx) {
5350
5409
  confidence: deriveConfidence4(fm, u),
5351
5410
  ...fm.status === "draft" || fm.status === "proposed" ? { unverified: true } : {},
5352
5411
  read_count: u.read_count,
5412
+ impact_score: imp.score,
5413
+ impact_tier: imp.tier,
5353
5414
  reasons: [reason],
5354
5415
  match_quality: matchQuality ?? "partial",
5355
5416
  ...score !== void 0 ? { semantic_score: score } : {},
@@ -5393,11 +5454,28 @@ async function getBriefing(input, ctx) {
5393
5454
  }
5394
5455
  }
5395
5456
  }
5457
+ const activatedSkills = /* @__PURE__ */ new Set();
5458
+ for (const [id, m] of seen) {
5459
+ if (m.type !== "skill") continue;
5460
+ const loaded = byId.get(id);
5461
+ if (!loaded) continue;
5462
+ const act = evaluateSkillActivation(loaded.memory.frontmatter, {
5463
+ task: input.task,
5464
+ files: input.files
5465
+ });
5466
+ if (act.applicable && !act.activated) {
5467
+ seen.delete(id);
5468
+ continue;
5469
+ }
5470
+ if (act.applicable && act.activated) activatedSkills.add(id);
5471
+ }
5396
5472
  const ranked = [...seen.values()].sort((a, b) => {
5397
5473
  const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
5398
5474
  const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
5399
- const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
5400
- const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
5475
+ const impactScore = (m) => (m.impact_score ?? 0) * 3;
5476
+ const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
5477
+ const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + (a.semantic_score ?? 0);
5478
+ const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + (b.semantic_score ?? 0);
5401
5479
  return sb - sa;
5402
5480
  });
5403
5481
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -5413,7 +5491,7 @@ async function getBriefing(input, ctx) {
5413
5491
  memories.push(...ranked.slice(0, briefingMaxMemories));
5414
5492
  if (input.track && memories.length > 0) {
5415
5493
  await trackReads3(ctx.paths, memories.map((m) => m.id));
5416
- const freshUsage = await loadUsageIndex7(ctx.paths);
5494
+ const freshUsage = await loadUsageIndex8(ctx.paths);
5417
5495
  const cfg = await loadConfig3(ctx.paths);
5418
5496
  const rule = {
5419
5497
  minReads: cfg.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads,
@@ -5422,7 +5500,7 @@ async function getBriefing(input, ctx) {
5422
5500
  for (const m of memories) {
5423
5501
  const loaded = byId.get(m.id);
5424
5502
  if (!loaded) continue;
5425
- const u = getUsage5(freshUsage, m.id);
5503
+ const u = getUsage6(freshUsage, m.id);
5426
5504
  if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
5427
5505
  const newFm = { ...loaded.memory.frontmatter, status: "validated" };
5428
5506
  try {
@@ -5434,12 +5512,12 @@ async function getBriefing(input, ctx) {
5434
5512
  }
5435
5513
  }
5436
5514
  }
5437
- const projectContextRaw = input.include_project_context && existsSync19(ctx.paths.projectContext) ? await readFile42(ctx.paths.projectContext, "utf8") : "";
5515
+ const projectContextRaw = input.include_project_context && existsSync20(ctx.paths.projectContext) ? await readFile42(ctx.paths.projectContext, "utf8") : "";
5438
5516
  const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
5439
5517
  const setupWarnings = [];
5440
5518
  let autoContextGenerated = false;
5441
5519
  let projectContext = isTemplateContext ? "" : projectContextRaw;
5442
- if ((isTemplateContext || !existsSync19(ctx.paths.projectContext)) && input.include_project_context) {
5520
+ if ((isTemplateContext || !existsSync20(ctx.paths.projectContext)) && input.include_project_context) {
5443
5521
  const haiveConfig = await loadConfig3(ctx.paths);
5444
5522
  if (haiveConfig.autoContext) {
5445
5523
  const codeMap = await loadCodeMap5(ctx.paths);
@@ -5535,7 +5613,7 @@ ${m.content}`).join("\n\n---\n\n"),
5535
5613
  const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
5536
5614
  const decayWarnings = [];
5537
5615
  for (const m of trimmedMemories) {
5538
- const u = getUsage5(usage, m.id);
5616
+ const u = getUsage6(usage, m.id);
5539
5617
  const loaded = byId.get(m.id);
5540
5618
  const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
5541
5619
  if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
@@ -5600,8 +5678,8 @@ ${m.content}`).join("\n\n---\n\n"),
5600
5678
  actionRequired.push(extractActionItem(m.id, loaded.memory.body));
5601
5679
  }
5602
5680
  }
5603
- if (existsSync19(ctx.paths.memoriesDir)) {
5604
- const allMems = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
5681
+ if (existsSync20(ctx.paths.memoriesDir)) {
5682
+ const allMems = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
5605
5683
  for (const { memory: memory2 } of allMems) {
5606
5684
  const fm = memory2.frontmatter;
5607
5685
  if (!fm.requires_human_approval) continue;
@@ -5611,7 +5689,7 @@ ${m.content}`).join("\n\n---\n\n"),
5611
5689
  }
5612
5690
  }
5613
5691
  const pendingDistillFile = pendingDistillPath(ctx);
5614
- if (existsSync19(pendingDistillFile)) {
5692
+ if (existsSync20(pendingDistillFile)) {
5615
5693
  try {
5616
5694
  const raw = await readFile42(pendingDistillFile, "utf8");
5617
5695
  const pd = JSON.parse(raw);
@@ -5640,7 +5718,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5640
5718
  }
5641
5719
  }
5642
5720
  const memoriesEmpty = outputMemories.length === 0;
5643
- const hasMemoriesDir = existsSync19(ctx.paths.memoriesDir);
5721
+ const hasMemoriesDir = existsSync20(ctx.paths.memoriesDir);
5644
5722
  const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
5645
5723
  const hasUnguessableSignal = outputMemories.some(
5646
5724
  (m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore2(m.body) >= GUESSABLE_THRESHOLD
@@ -5687,7 +5765,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5687
5765
  "No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
5688
5766
  );
5689
5767
  }
5690
- if (existsSync19(ctx.paths.haiveDir)) {
5768
+ if (existsSync20(ctx.paths.haiveDir)) {
5691
5769
  await writeBriefingMarker2(ctx.paths, {
5692
5770
  sessionId: process.env.HAIVE_SESSION_ID,
5693
5771
  ...input.task ? { task: input.task } : {},
@@ -5736,17 +5814,17 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5736
5814
  };
5737
5815
  }
5738
5816
  var CodeMapInputSchema = {
5739
- file: z18.string().optional().describe("Filter to files whose path contains this substring"),
5740
- symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
5741
- paths: z18.array(z18.string()).default([]).describe(
5817
+ file: z19.string().optional().describe("Filter to files whose path contains this substring"),
5818
+ symbol: z19.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
5819
+ paths: z19.array(z19.string()).default([]).describe(
5742
5820
  "Filter to files under any of these path prefixes (e.g. ['packages/mcp/src/tools/', 'src/auth/']). OR-joined with `file` substring; useful to get a focused view of one module."
5743
5821
  ),
5744
- max_files: z18.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
5745
- max_tokens: z18.number().int().positive().optional().describe(
5822
+ max_files: z19.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
5823
+ max_tokens: z19.number().int().positive().optional().describe(
5746
5824
  "Approximate token budget for the response. When the matching set exceeds it, files are ranked by export density (exports per LOC) and the highest-signal ones are kept first. Omit to disable budgeting (legacy behavior)."
5747
5825
  )
5748
5826
  };
5749
- var CodeMapInputZod = z18.object(CodeMapInputSchema);
5827
+ var CodeMapInputZod = z19.object(CodeMapInputSchema);
5750
5828
  async function codeMapTool(input, ctx) {
5751
5829
  const map = await loadCodeMap22(ctx.paths);
5752
5830
  if (!map) {
@@ -5814,14 +5892,14 @@ function estimateFileEntryTokens(f) {
5814
5892
  return estimateTokens2(f.path) + estimateTokens2(f.entry.summary ?? "") + exportsCost + 4;
5815
5893
  }
5816
5894
  var MemDiffInputSchema = {
5817
- id_a: z19.string().min(1).describe("First memory id"),
5818
- id_b: z19.string().min(1).describe("Second memory id")
5895
+ id_a: z20.string().min(1).describe("First memory id"),
5896
+ id_b: z20.string().min(1).describe("Second memory id")
5819
5897
  };
5820
5898
  async function memDiff(input, ctx) {
5821
- if (!existsSync20(ctx.paths.memoriesDir)) {
5899
+ if (!existsSync21(ctx.paths.memoriesDir)) {
5822
5900
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
5823
5901
  }
5824
- const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
5902
+ const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
5825
5903
  const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
5826
5904
  const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
5827
5905
  if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
@@ -5854,15 +5932,15 @@ async function memDiff(input, ctx) {
5854
5932
  };
5855
5933
  }
5856
5934
  var GetRecapInputSchema = {
5857
- scope: z20.enum(["personal", "team", "any"]).default("any").describe(
5935
+ scope: z21.enum(["personal", "team", "any"]).default("any").describe(
5858
5936
  "Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
5859
5937
  )
5860
5938
  };
5861
5939
  async function getRecap(input, ctx) {
5862
- if (!existsSync21(ctx.paths.memoriesDir)) {
5940
+ if (!existsSync222(ctx.paths.memoriesDir)) {
5863
5941
  return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
5864
5942
  }
5865
- const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
5943
+ const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
5866
5944
  const recaps = all.filter(({ memory: memory2 }) => memory2.frontmatter.type === "session_recap").filter(({ memory: memory2 }) => input.scope === "any" || memory2.frontmatter.scope === input.scope).sort(
5867
5945
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
5868
5946
  );
@@ -5885,11 +5963,11 @@ async function getRecap(input, ctx) {
5885
5963
  };
5886
5964
  }
5887
5965
  var MemRelevantToInputSchema = {
5888
- task: z21.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
5889
- files: z21.array(z21.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
5890
- limit: z21.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
5891
- min_semantic_score: z21.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
5892
- format: z21.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
5966
+ task: z22.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
5967
+ files: z22.array(z22.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
5968
+ limit: z22.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
5969
+ min_semantic_score: z22.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
5970
+ format: z22.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
5893
5971
  };
5894
5972
  async function memRelevantTo(input, ctx) {
5895
5973
  const briefingInput = {
@@ -5918,11 +5996,11 @@ async function memRelevantTo(input, ctx) {
5918
5996
  return out;
5919
5997
  }
5920
5998
  var CodeSearchInputSchema = {
5921
- query: z22.string().min(1).describe(
5999
+ query: z23.string().min(1).describe(
5922
6000
  "Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
5923
6001
  ),
5924
- k: z22.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
5925
- min_score: z22.number().min(0).max(1).default(0.2).describe(
6002
+ k: z23.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
6003
+ min_score: z23.number().min(0).max(1).default(0.2).describe(
5926
6004
  "Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
5927
6005
  )
5928
6006
  };
@@ -5951,14 +6029,14 @@ async function codeSearch(input, ctx) {
5951
6029
  return { available: true, hits: result.hits };
5952
6030
  }
5953
6031
  var WhyThisFileInputSchema = {
5954
- path: z23.string().min(1).describe(
6032
+ path: z24.string().min(1).describe(
5955
6033
  "Project-relative path to the file you want context on (e.g. 'packages/mcp/src/tools/mem-save.ts')."
5956
6034
  ),
5957
- git_log_limit: z23.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
5958
- memory_limit: z23.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
6035
+ git_log_limit: z24.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
6036
+ memory_limit: z24.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
5959
6037
  };
5960
6038
  async function whyThisFile(input, ctx) {
5961
- const fileExists = existsSync222(path102.join(ctx.paths.root, input.path));
6039
+ const fileExists = existsSync23(path102.join(ctx.paths.root, input.path));
5962
6040
  const [commits, memories, codeMap] = await Promise.all([
5963
6041
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
5964
6042
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -5999,16 +6077,16 @@ async function whyThisFile(input, ctx) {
5999
6077
  };
6000
6078
  }
6001
6079
  async function collectAnchoredMemories(ctx, filePath, limit) {
6002
- if (!existsSync222(ctx.paths.memoriesDir)) return [];
6003
- const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
6004
- const usage = await loadUsageIndex8(ctx.paths);
6080
+ if (!existsSync23(ctx.paths.memoriesDir)) return [];
6081
+ const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
6082
+ const usage = await loadUsageIndex9(ctx.paths);
6005
6083
  const out = [];
6006
6084
  for (const { memory: memory2 } of all) {
6007
6085
  const fm = memory2.frontmatter;
6008
6086
  if (fm.status === "rejected" || fm.status === "deprecated") continue;
6009
6087
  if (fm.type === "session_recap") continue;
6010
6088
  if (!memoryMatchesAnchorPaths3(memory2, [filePath])) continue;
6011
- const u = getUsage6(usage, fm.id);
6089
+ const u = getUsage7(usage, fm.id);
6012
6090
  out.push({
6013
6091
  id: fm.id,
6014
6092
  type: fm.type,
@@ -6053,17 +6131,17 @@ function runCommand(cmd, args, cwd) {
6053
6131
  });
6054
6132
  }
6055
6133
  var AntiPatternsCheckInputSchema = {
6056
- diff: z24.string().optional().describe(
6134
+ diff: z25.string().optional().describe(
6057
6135
  "Raw unified diff text (or any code/text snippet) to scan for previously documented anti-patterns. Tokens from the diff are used to match memory bodies and the embeddings index."
6058
6136
  ),
6059
- paths: z24.array(z24.string()).default([]).describe(
6137
+ paths: z25.array(z25.string()).default([]).describe(
6060
6138
  "File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
6061
6139
  ),
6062
- limit: z24.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
6063
- semantic: z24.boolean().default(true).describe(
6140
+ limit: z25.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
6141
+ semantic: z25.boolean().default(true).describe(
6064
6142
  "When true, also use semantic search (requires @hiveai/embeddings + memory index) to find related anti-patterns."
6065
6143
  ),
6066
- min_semantic_score: z24.number().min(0).max(1).default(0.45).describe(
6144
+ min_semantic_score: z25.number().min(0).max(1).default(0.45).describe(
6067
6145
  "Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
6068
6146
  )
6069
6147
  };
@@ -6131,10 +6209,10 @@ async function antiPatternsCheck(input, ctx) {
6131
6209
  notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
6132
6210
  };
6133
6211
  }
6134
- if (!existsSync23(ctx.paths.memoriesDir)) {
6212
+ if (!existsSync24(ctx.paths.memoriesDir)) {
6135
6213
  return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
6136
6214
  }
6137
- const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
6215
+ const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
6138
6216
  const minSemanticScore = input.min_semantic_score ?? 0.45;
6139
6217
  const negative = all.filter(({ memory: memory2 }) => {
6140
6218
  const t = memory2.frontmatter.type;
@@ -6145,7 +6223,7 @@ async function antiPatternsCheck(input, ctx) {
6145
6223
  if (negative.length === 0) {
6146
6224
  return { scanned: 0, warnings: [], notice: "No attempt/gotcha memories found yet." };
6147
6225
  }
6148
- const usage = await loadUsageIndex9(ctx.paths);
6226
+ const usage = await loadUsageIndex10(ctx.paths);
6149
6227
  const seen = /* @__PURE__ */ new Map();
6150
6228
  const upsert = (fm, body, reason, score) => {
6151
6229
  const existing = seen.get(fm.id);
@@ -6156,7 +6234,7 @@ async function antiPatternsCheck(input, ctx) {
6156
6234
  }
6157
6235
  return;
6158
6236
  }
6159
- const u = getUsage7(usage, fm.id);
6237
+ const u = getUsage8(usage, fm.id);
6160
6238
  seen.set(fm.id, {
6161
6239
  id: fm.id,
6162
6240
  type: fm.type,
@@ -6234,12 +6312,12 @@ async function antiPatternsCheck(input, ctx) {
6234
6312
  };
6235
6313
  }
6236
6314
  var MemDistillInputSchema = {
6237
- since_days: z25.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
6238
- min_cluster: z25.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
6239
- type_filter: z25.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
6315
+ since_days: z26.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
6316
+ min_cluster: z26.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
6317
+ type_filter: z26.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
6240
6318
  "Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
6241
6319
  ),
6242
- scope: z25.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
6320
+ scope: z26.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
6243
6321
  };
6244
6322
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
6245
6323
  var STOP_WORDS = /* @__PURE__ */ new Set([
@@ -6279,11 +6357,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
6279
6357
  "error"
6280
6358
  ]);
6281
6359
  async function memDistill(input, ctx) {
6282
- if (!existsSync24(ctx.paths.memoriesDir)) {
6360
+ if (!existsSync25(ctx.paths.memoriesDir)) {
6283
6361
  return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
6284
6362
  }
6285
6363
  const cutoff = Date.now() - input.since_days * MS_PER_DAY;
6286
- const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
6364
+ const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
6287
6365
  const candidates = all.filter(({ memory: memory2 }) => {
6288
6366
  const fm = memory2.frontmatter;
6289
6367
  if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
@@ -6386,11 +6464,11 @@ function firstHeading(body) {
6386
6464
  return void 0;
6387
6465
  }
6388
6466
  var WhyThisDecisionInputSchema = {
6389
- id: z26.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
6390
- git_log_limit: z26.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
6467
+ id: z27.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
6468
+ git_log_limit: z27.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
6391
6469
  };
6392
6470
  async function whyThisDecision(input, ctx) {
6393
- if (!existsSync25(ctx.paths.memoriesDir)) {
6471
+ if (!existsSync26(ctx.paths.memoriesDir)) {
6394
6472
  return {
6395
6473
  found: false,
6396
6474
  related: [],
@@ -6399,8 +6477,8 @@ async function whyThisDecision(input, ctx) {
6399
6477
  notice: "No .ai/memories directory."
6400
6478
  };
6401
6479
  }
6402
- const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
6403
- const usage = await loadUsageIndex10(ctx.paths);
6480
+ const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
6481
+ const usage = await loadUsageIndex11(ctx.paths);
6404
6482
  const target = all.find(({ memory: memory2 }) => memory2.frontmatter.id === input.id);
6405
6483
  if (!target) {
6406
6484
  return {
@@ -6412,7 +6490,7 @@ async function whyThisDecision(input, ctx) {
6412
6490
  };
6413
6491
  }
6414
6492
  const fm = target.memory.frontmatter;
6415
- const targetUsage = getUsage8(usage, fm.id);
6493
+ const targetUsage = getUsage9(usage, fm.id);
6416
6494
  const decision = {
6417
6495
  id: fm.id,
6418
6496
  type: fm.type,
@@ -6429,7 +6507,7 @@ async function whyThisDecision(input, ctx) {
6429
6507
  const isExplicit = relatedSet.has(memory2.frontmatter.id);
6430
6508
  const isBackLink = (memory2.frontmatter.related_ids ?? []).includes(fm.id);
6431
6509
  if (!isExplicit && !isBackLink) continue;
6432
- const u = getUsage8(usage, memory2.frontmatter.id);
6510
+ const u = getUsage9(usage, memory2.frontmatter.id);
6433
6511
  related.push({
6434
6512
  id: memory2.frontmatter.id,
6435
6513
  type: memory2.frontmatter.type,
@@ -6449,7 +6527,7 @@ async function whyThisDecision(input, ctx) {
6449
6527
  (p) => targetPaths.some((tp) => singlePathsOverlap(p, tp))
6450
6528
  );
6451
6529
  if (overlappingPaths.length === 0) continue;
6452
- const u = getUsage8(usage, memory2.frontmatter.id);
6530
+ const u = getUsage9(usage, memory2.frontmatter.id);
6453
6531
  path_neighbors.push({
6454
6532
  id: memory2.frontmatter.id,
6455
6533
  type: memory2.frontmatter.type,
@@ -6521,22 +6599,22 @@ function runCommand2(cmd, args, cwd) {
6521
6599
  });
6522
6600
  }
6523
6601
  var MemConflictsInputSchema = {
6524
- id: z27.string().min(1).describe("Memory id to check for conflicts."),
6525
- min_score: z27.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
6526
- semantic: z27.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
6602
+ id: z28.string().min(1).describe("Memory id to check for conflicts."),
6603
+ min_score: z28.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
6604
+ semantic: z28.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
6527
6605
  };
6528
6606
  var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
6529
6607
  var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
6530
6608
  async function memConflicts(input, ctx) {
6531
- if (!existsSync26(ctx.paths.memoriesDir)) {
6609
+ if (!existsSync27(ctx.paths.memoriesDir)) {
6532
6610
  return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
6533
6611
  }
6534
- const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
6612
+ const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
6535
6613
  const target = all.find(({ memory: memory2 }) => memory2.frontmatter.id === input.id);
6536
6614
  if (!target) {
6537
6615
  return { found: false, scanned: 0, conflicts: [], notice: `Memory '${input.id}' not found.` };
6538
6616
  }
6539
- const usage = await loadUsageIndex11(ctx.paths);
6617
+ const usage = await loadUsageIndex12(ctx.paths);
6540
6618
  const others = all.filter(
6541
6619
  ({ memory: memory2 }) => memory2.frontmatter.id !== input.id && memory2.frontmatter.type !== "session_recap"
6542
6620
  );
@@ -6577,7 +6655,7 @@ async function memConflicts(input, ctx) {
6577
6655
  }
6578
6656
  }
6579
6657
  if (reasons.length === 0) continue;
6580
- const u = getUsage9(usage, fm.id);
6658
+ const u = getUsage10(usage, fm.id);
6581
6659
  conflicts.push({
6582
6660
  id: fm.id,
6583
6661
  type: fm.type,
@@ -6642,15 +6720,15 @@ async function trySemanticSimilarities(ctx, target, others) {
6642
6720
  return map;
6643
6721
  }
6644
6722
  var PreCommitCheckInputSchema = {
6645
- diff: z28.string().optional().describe(
6723
+ diff: z29.string().optional().describe(
6646
6724
  "Raw unified diff text to scan. If omitted, only `paths` is used. When called from a pre-commit hook, pipe the output of `git diff --cached`."
6647
6725
  ),
6648
- paths: z28.array(z28.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
6649
- block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
6726
+ paths: z29.array(z29.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
6727
+ block_on: z29.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
6650
6728
  "When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
6651
6729
  ),
6652
- semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
6653
- anchored_blocks: z28.boolean().default(false).describe(
6730
+ semantic: z29.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
6731
+ anchored_blocks: z29.boolean().default(false).describe(
6654
6732
  "When true, ALSO block a high-confidence anti-pattern (attempt/gotcha) that is anchored to a touched file AND corroborated by the diff (literal token overlap, or semantic >= 0.45) \u2014 not just very strong semantic matches. Powers the 'anchored' enforcement gate. Config/docs-only commits are still downgraded. Default false preserves the soft, semantic-only blocking behavior."
6655
6733
  )
6656
6734
  };
@@ -6951,12 +7029,12 @@ var CONFIG_PATTERNS = [
6951
7029
  var MAX_DIFF_BYTES = 4096;
6952
7030
  var HOT_FILE_MIN = 3;
6953
7031
  var PatternDetectInputSchema = {
6954
- since_days: z29.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
6955
- dry_run: z29.boolean().default(false).describe("When true, report matches without writing any memory files."),
6956
- scope: z29.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
7032
+ since_days: z30.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
7033
+ dry_run: z30.boolean().default(false).describe("When true, report matches without writing any memory files."),
7034
+ scope: z30.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
6957
7035
  };
6958
7036
  async function patternDetect(input, ctx) {
6959
- if (!existsSync27(ctx.paths.haiveDir)) {
7037
+ if (!existsSync28(ctx.paths.haiveDir)) {
6960
7038
  return {
6961
7039
  scanned_events: 0,
6962
7040
  matches: [],
@@ -7085,7 +7163,7 @@ async function patternDetect(input, ctx) {
7085
7163
  fm.id,
7086
7164
  void 0
7087
7165
  );
7088
- if (existsSync27(file)) continue;
7166
+ if (existsSync28(file)) continue;
7089
7167
  await mkdir72(path112.dirname(file), { recursive: true });
7090
7168
  await writeFile12(
7091
7169
  file,
@@ -7130,17 +7208,17 @@ function gitFileDiff(root, file, sinceDays) {
7130
7208
  }
7131
7209
  }
7132
7210
  var MemConflictCandidatesInputSchema = {
7133
- since_days: z30.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
7134
- types: z30.array(z30.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
7135
- min_jaccard: z30.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
7136
- max_pairs: z30.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
7137
- max_scan: z30.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
7138
- max_topic_pairs: z30.number().int().positive().max(100).default(20).describe(
7211
+ since_days: z31.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
7212
+ types: z31.array(z31.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
7213
+ min_jaccard: z31.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
7214
+ max_pairs: z31.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
7215
+ max_scan: z31.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
7216
+ max_topic_pairs: z31.number().int().positive().max(100).default(20).describe(
7139
7217
  "Cap for extra signal: memories sharing the same topic with validated vs rejected status."
7140
7218
  )
7141
7219
  };
7142
7220
  async function memConflictCandidates(input, ctx) {
7143
- if (!existsSync28(ctx.paths.memoriesDir)) {
7221
+ if (!existsSync29(ctx.paths.memoriesDir)) {
7144
7222
  return {
7145
7223
  pairs: [],
7146
7224
  topic_status_pairs: [],
@@ -7149,7 +7227,7 @@ async function memConflictCandidates(input, ctx) {
7149
7227
  notice: "No .ai/memories directory."
7150
7228
  };
7151
7229
  }
7152
- const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
7230
+ const all = await loadMemoriesFromDir222(ctx.paths.memoriesDir);
7153
7231
  const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
7154
7232
  sinceDays: input.since_days,
7155
7233
  types: input.types,
@@ -7162,7 +7240,7 @@ async function memConflictCandidates(input, ctx) {
7162
7240
  return { pairs, topic_status_pairs: topicStatusPairs, scanned, truncated, notice };
7163
7241
  }
7164
7242
  var MemResolveProjectInputSchema = {
7165
- cwd: z31.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
7243
+ cwd: z32.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
7166
7244
  };
7167
7245
  async function memResolveProject(input, _ctx) {
7168
7246
  void _ctx;
@@ -7175,7 +7253,7 @@ async function memResolveProject(input, _ctx) {
7175
7253
  }
7176
7254
  var MemSuggestTopicInputSchema = {
7177
7255
  type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
7178
- title: z32.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
7256
+ title: z33.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
7179
7257
  };
7180
7258
  async function memSuggestTopic(input, _ctx) {
7181
7259
  void _ctx;
@@ -7183,15 +7261,15 @@ async function memSuggestTopic(input, _ctx) {
7183
7261
  return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
7184
7262
  }
7185
7263
  var MemTimelineInputSchema = {
7186
- memory_id: z33.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
7187
- topic: z33.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
7188
- limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
7264
+ memory_id: z34.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
7265
+ topic: z34.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
7266
+ limit: z34.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
7189
7267
  };
7190
7268
  async function memTimeline(input, ctx) {
7191
- if (!existsSync29(ctx.paths.memoriesDir)) {
7269
+ if (!existsSync30(ctx.paths.memoriesDir)) {
7192
7270
  return { entries: [], total: 0, notice: "No .ai/memories directory." };
7193
7271
  }
7194
- const all = await loadMemoriesFromDir222(ctx.paths.memoriesDir);
7272
+ const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
7195
7273
  const { entries, notice } = collectTimelineEntries(all, {
7196
7274
  memoryId: input.memory_id,
7197
7275
  topic: input.topic,
@@ -7200,9 +7278,9 @@ async function memTimeline(input, ctx) {
7200
7278
  return { entries, total: entries.length, notice };
7201
7279
  }
7202
7280
  var RuntimeJournalAppendInputSchema = {
7203
- message: z34.string().min(1).describe("Short line to append to the runtime session journal"),
7204
- kind: z34.enum(["note", "session_end", "mcp"]).default("note"),
7205
- tool: z34.string().optional().describe("When kind=mcp, which tool name (optional)")
7281
+ message: z35.string().min(1).describe("Short line to append to the runtime session journal"),
7282
+ kind: z35.enum(["note", "session_end", "mcp"]).default("note"),
7283
+ tool: z35.string().optional().describe("When kind=mcp, which tool name (optional)")
7206
7284
  };
7207
7285
  async function runtimeJournalAppend(input, ctx) {
7208
7286
  await appendRuntimeJournalEntry2(ctx.paths, {
@@ -7216,7 +7294,7 @@ async function runtimeJournalAppend(input, ctx) {
7216
7294
  };
7217
7295
  }
7218
7296
  var RuntimeJournalTailInputSchema = {
7219
- limit: z35.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
7297
+ limit: z36.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
7220
7298
  };
7221
7299
  async function runtimeJournalTail(input, ctx) {
7222
7300
  const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
@@ -7226,10 +7304,10 @@ async function runtimeJournalTail(input, ctx) {
7226
7304
  return { entries };
7227
7305
  }
7228
7306
  var BootstrapProjectArgsSchema = {
7229
- module: z36.string().optional().describe(
7307
+ module: z37.string().optional().describe(
7230
7308
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
7231
7309
  ),
7232
- focus: z36.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
7310
+ focus: z37.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
7233
7311
  };
7234
7312
  var ROOT_TEMPLATE = `# Project context
7235
7313
 
@@ -7310,8 +7388,8 @@ ${template}\`\`\`
7310
7388
  };
7311
7389
  }
7312
7390
  var PostTaskArgsSchema = {
7313
- task_summary: z37.string().optional().describe("One sentence describing what you just did"),
7314
- files_touched: z37.array(z37.string()).optional().describe("Files you created or modified during the task")
7391
+ task_summary: z38.string().optional().describe("One sentence describing what you just did"),
7392
+ files_touched: z38.array(z38.string()).optional().describe("Files you created or modified during the task")
7315
7393
  };
7316
7394
  function postTaskPrompt(args, ctx) {
7317
7395
  const taskLine = args.task_summary ? `
@@ -7406,10 +7484,10 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
7406
7484
  };
7407
7485
  }
7408
7486
  var ImportDocsArgsSchema = {
7409
- content: z38.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
7410
- source: z38.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
7411
- scope: z38.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
7412
- dry_run: z38.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
7487
+ content: z39.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
7488
+ source: z39.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
7489
+ scope: z39.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
7490
+ dry_run: z39.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
7413
7491
  };
7414
7492
  function importDocsPrompt(args, ctx) {
7415
7493
  const sourceLine = args.source ? `
@@ -7472,7 +7550,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7472
7550
  };
7473
7551
  }
7474
7552
  var SERVER_NAME = "haive";
7475
- var SERVER_VERSION = "0.10.9";
7553
+ var SERVER_VERSION = "0.11.0";
7476
7554
  function jsonResult(data) {
7477
7555
  return {
7478
7556
  content: [
@@ -7514,7 +7592,8 @@ var MAINTENANCE_PROFILE_TOOLS = [
7514
7592
  "anti_patterns_check",
7515
7593
  "mem_distill",
7516
7594
  "mem_timeline",
7517
- "mem_conflict_candidates"
7595
+ "mem_conflict_candidates",
7596
+ "mem_feedback"
7518
7597
  ];
7519
7598
  var EXPERIMENTAL_PROFILE_TOOLS = [
7520
7599
  ...MAINTENANCE_PROFILE_TOOLS,
@@ -7546,6 +7625,7 @@ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
7546
7625
  "mem_approve",
7547
7626
  "mem_reject",
7548
7627
  "mem_delete",
7628
+ "mem_feedback",
7549
7629
  "runtime_journal_append",
7550
7630
  "pattern_detect"
7551
7631
  ]);
@@ -8041,6 +8121,28 @@ function createHaiveServer(options = {}) {
8041
8121
  MemRejectInputSchema,
8042
8122
  async (input) => jsonResult(await memReject(input, context))
8043
8123
  );
8124
+ registerTool(
8125
+ "mem_feedback",
8126
+ [
8127
+ "Record whether a memory actually HELPED \u2014 the closed-loop utility signal.",
8128
+ "",
8129
+ "USE right after a memory changed (or failed to change) what you did:",
8130
+ " - outcome='applied' \u2192 the memory steered your work (strong positive signal)",
8131
+ " - outcome='rejected' \u2192 it was wrong/outdated/unhelpful (negative signal)",
8132
+ "",
8133
+ "A read only means a memory was surfaced; 'applied' means it demonstrably helped.",
8134
+ "This powers `haive memory impact` (impact tiers + prune candidates) and future ranking.",
8135
+ "",
8136
+ "PARAMETERS:",
8137
+ " id \u2014 full memory id the feedback is about",
8138
+ " outcome \u2014 'applied' | 'rejected'",
8139
+ " reason \u2014 why it was rejected (optional, stored on the usage record)",
8140
+ "",
8141
+ "RETURNS: { ok, id, outcome, usage:{read_count,applied_count,rejected_count}, impact:{score,tier,signals} }"
8142
+ ].join("\n"),
8143
+ MemFeedbackInputSchema,
8144
+ async (input) => jsonResult(await memFeedback(input, context))
8145
+ );
8044
8146
  registerTool(
8045
8147
  "mem_pending",
8046
8148
  [
@@ -8447,21 +8549,21 @@ function registerMcp(program2) {
8447
8549
  // src/commands/sync.ts
8448
8550
  import { spawnSync as spawnSync4 } from "child_process";
8449
8551
  import { readFile as readFile9, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
8450
- import { existsSync as existsSync30 } from "fs";
8552
+ import { existsSync as existsSync31 } from "fs";
8451
8553
  import path15 from "path";
8452
8554
  import "commander";
8453
8555
  import {
8454
8556
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
8455
8557
  buildFrontmatter as buildFrontmatter6,
8456
8558
  findProjectRoot as findProjectRoot12,
8457
- getUsage as getUsage10,
8559
+ getUsage as getUsage11,
8458
8560
  isAutoPromoteEligible as isAutoPromoteEligible2,
8459
8561
  isDecaying as isDecaying2,
8460
8562
  isStackPackSeed as isStackPackSeed3,
8461
8563
  loadCodeMap as loadCodeMap6,
8462
8564
  loadConfig as loadConfig4,
8463
- loadMemoriesFromDir as loadMemoriesFromDir23,
8464
- loadUsageIndex as loadUsageIndex12,
8565
+ loadMemoriesFromDir as loadMemoriesFromDir24,
8566
+ loadUsageIndex as loadUsageIndex13,
8465
8567
  pullCrossRepoSources,
8466
8568
  resolveHaivePaths as resolveHaivePaths9,
8467
8569
  resolveManifestFiles,
@@ -8484,7 +8586,7 @@ function registerSync(program2) {
8484
8586
  ).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").option("--dry-run", "report what would change without writing any files").action(async (opts) => {
8485
8587
  const root = findProjectRoot12(opts.dir);
8486
8588
  const paths = resolveHaivePaths9(root);
8487
- if (!existsSync30(paths.memoriesDir)) {
8589
+ if (!existsSync31(paths.memoriesDir)) {
8488
8590
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8489
8591
  process.exitCode = 1;
8490
8592
  return;
@@ -8503,7 +8605,7 @@ function registerSync(program2) {
8503
8605
  let promoted = 0;
8504
8606
  let autoApproved = 0;
8505
8607
  if (opts.verify !== false) {
8506
- const memories = await loadMemoriesFromDir23(paths.memoriesDir);
8608
+ const memories = await loadMemoriesFromDir24(paths.memoriesDir);
8507
8609
  for (const { memory: memory2, filePath } of memories) {
8508
8610
  if (memory2.frontmatter.type === "session_recap") {
8509
8611
  if (memory2.frontmatter.status === "stale") {
@@ -8570,13 +8672,13 @@ function registerSync(program2) {
8570
8672
  }
8571
8673
  }
8572
8674
  if (opts.promote !== false) {
8573
- const memories = await loadMemoriesFromDir23(paths.memoriesDir);
8574
- const usage = await loadUsageIndex12(paths);
8675
+ const memories = await loadMemoriesFromDir24(paths.memoriesDir);
8676
+ const usage = await loadUsageIndex13(paths);
8575
8677
  const nowMs = Date.now();
8576
8678
  for (const { memory: memory2, filePath } of memories) {
8577
8679
  const fm = memory2.frontmatter;
8578
8680
  if (fm.type === "session_recap") continue;
8579
- if (isAutoPromoteEligible2(fm, getUsage10(usage, fm.id), {
8681
+ if (isAutoPromoteEligible2(fm, getUsage11(usage, fm.id), {
8580
8682
  minReads: autoPromoteMinReads,
8581
8683
  maxRejections: DEFAULT_AUTO_PROMOTE_RULE2.maxRejections
8582
8684
  })) {
@@ -8622,7 +8724,7 @@ function registerSync(program2) {
8622
8724
  for (const repair of repairs) log(ui.dim(`autopilot: ${repair.message}`));
8623
8725
  }
8624
8726
  const sinceReport = opts.since ? collectSinceChanges(root, opts.since) : null;
8625
- const draftMemories = (await loadMemoriesFromDir23(paths.memoriesDir)).filter(
8727
+ const draftMemories = (await loadMemoriesFromDir24(paths.memoriesDir)).filter(
8626
8728
  (m) => m.memory.frontmatter.status === "draft"
8627
8729
  );
8628
8730
  const draftCount = draftMemories.length;
@@ -8657,12 +8759,12 @@ function registerSync(program2) {
8657
8759
  }
8658
8760
  }
8659
8761
  if (!opts.quiet) {
8660
- const allForDecay = await loadMemoriesFromDir23(paths.memoriesDir);
8661
- const usageForDecay = await loadUsageIndex12(paths);
8762
+ const allForDecay = await loadMemoriesFromDir24(paths.memoriesDir);
8763
+ const usageForDecay = await loadUsageIndex13(paths);
8662
8764
  const decaying = allForDecay.filter(({ memory: memory2 }) => {
8663
8765
  const fm = memory2.frontmatter;
8664
8766
  if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
8665
- const u = getUsage10(usageForDecay, fm.id);
8767
+ const u = getUsage11(usageForDecay, fm.id);
8666
8768
  return isDecaying2(u, fm.created_at);
8667
8769
  });
8668
8770
  if (decaying.length > 0) {
@@ -8908,8 +9010,8 @@ function bridgeSummaryLine(body) {
8908
9010
  return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
8909
9011
  }
8910
9012
  async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8911
- if (!existsSync30(memoriesDir)) return;
8912
- const all = await loadMemoriesFromDir23(memoriesDir);
9013
+ if (!existsSync31(memoriesDir)) return;
9014
+ const all = await loadMemoriesFromDir24(memoriesDir);
8913
9015
  const top = all.filter(({ memory: memory2 }) => {
8914
9016
  const s = memory2.frontmatter.status;
8915
9017
  if (memory2.frontmatter.type === "session_recap") return false;
@@ -8934,7 +9036,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8934
9036
  ` + block + `
8935
9037
 
8936
9038
  ${BRIDGE_END}`;
8937
- const fileExists = existsSync30(bridgeFile);
9039
+ const fileExists = existsSync31(bridgeFile);
8938
9040
  let existing = fileExists ? await readFile9(bridgeFile, "utf8") : "";
8939
9041
  existing = existing.replace(/\r\n/g, "\n");
8940
9042
  const startIdx = existing.indexOf(BRIDGE_START);
@@ -8985,7 +9087,7 @@ function collectSinceChanges(root, ref) {
8985
9087
  // src/commands/memory-add.ts
8986
9088
  import { createHash as createHash2 } from "crypto";
8987
9089
  import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
8988
- import { existsSync as existsSync31 } from "fs";
9090
+ import { existsSync as existsSync33 } from "fs";
8989
9091
  import path16 from "path";
8990
9092
  import "commander";
8991
9093
  import {
@@ -8993,7 +9095,7 @@ import {
8993
9095
  findProjectRoot as findProjectRoot13,
8994
9096
  inferModulesFromPaths as inferModulesFromPaths3,
8995
9097
  loadConfig as loadConfig5,
8996
- loadMemoriesFromDir as loadMemoriesFromDir24,
9098
+ loadMemoriesFromDir as loadMemoriesFromDir25,
8997
9099
  memoryFilePath as memoryFilePath6,
8998
9100
  resolveHaivePaths as resolveHaivePaths10,
8999
9101
  serializeMemory as serializeMemory12,
@@ -9027,7 +9129,7 @@ function registerMemoryAdd(memory2) {
9027
9129
  ).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").option("--slug <slug>", "short kebab-case identifier used in the file name (auto-derived from --title/--body when omitted)").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: config default; team in autopilot)").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("-d, --dir <dir>", "project root").action(async (opts) => {
9028
9130
  const root = findProjectRoot13(opts.dir);
9029
9131
  const paths = resolveHaivePaths10(root);
9030
- if (!existsSync31(paths.haiveDir)) {
9132
+ if (!existsSync33(paths.haiveDir)) {
9031
9133
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9032
9134
  process.exitCode = 1;
9033
9135
  return;
@@ -9039,7 +9141,7 @@ function registerMemoryAdd(memory2) {
9039
9141
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
9040
9142
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
9041
9143
  if (anchorPaths.length > 0) {
9042
- const missing = anchorPaths.filter((p) => !existsSync31(path16.resolve(root, p)));
9144
+ const missing = anchorPaths.filter((p) => !existsSync33(path16.resolve(root, p)));
9043
9145
  if (missing.length > 0) {
9044
9146
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
9045
9147
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -9052,7 +9154,7 @@ function registerMemoryAdd(memory2) {
9052
9154
  const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
9053
9155
  let body;
9054
9156
  if (opts.bodyFile !== void 0) {
9055
- if (!existsSync31(opts.bodyFile)) {
9157
+ if (!existsSync33(opts.bodyFile)) {
9056
9158
  ui.error(`--body-file not found: ${opts.bodyFile}`);
9057
9159
  process.exitCode = 1;
9058
9160
  return;
@@ -9068,9 +9170,9 @@ TODO \u2014 write the memory body.
9068
9170
  `;
9069
9171
  }
9070
9172
  const scope = opts.scope ?? config.defaultScope ?? "personal";
9071
- if (existsSync31(paths.memoriesDir)) {
9173
+ if (existsSync33(paths.memoriesDir)) {
9072
9174
  const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
9073
- const allForHash = await loadMemoriesFromDir24(paths.memoriesDir);
9175
+ const allForHash = await loadMemoriesFromDir25(paths.memoriesDir);
9074
9176
  const hashDup = allForHash.find(
9075
9177
  ({ memory: memory3 }) => createHash2("sha256").update(memory3.body.trim()).digest("hex").slice(0, 12) === incomingHash && memory3.frontmatter.scope === scope
9076
9178
  );
@@ -9081,8 +9183,8 @@ TODO \u2014 write the memory body.
9081
9183
  return;
9082
9184
  }
9083
9185
  }
9084
- if (opts.topic && existsSync31(paths.memoriesDir)) {
9085
- const existing = await loadMemoriesFromDir24(paths.memoriesDir);
9186
+ if (opts.topic && existsSync33(paths.memoriesDir)) {
9187
+ const existing = await loadMemoriesFromDir25(paths.memoriesDir);
9086
9188
  const topicMatch = existing.find(
9087
9189
  ({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
9088
9190
  );
@@ -9126,13 +9228,13 @@ TODO \u2014 write the memory body.
9126
9228
  });
9127
9229
  const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9128
9230
  await mkdir11(path16.dirname(file), { recursive: true });
9129
- if (existsSync31(file)) {
9231
+ if (existsSync33(file)) {
9130
9232
  ui.error(`Memory already exists at ${file}`);
9131
9233
  process.exitCode = 1;
9132
9234
  return;
9133
9235
  }
9134
- if (existsSync31(paths.memoriesDir)) {
9135
- const existing = await loadMemoriesFromDir24(paths.memoriesDir);
9236
+ if (existsSync33(paths.memoriesDir)) {
9237
+ const existing = await loadMemoriesFromDir25(paths.memoriesDir);
9136
9238
  const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
9137
9239
  const similar = existing.filter(({ memory: memory3 }) => {
9138
9240
  const id = memory3.frontmatter.id.toLowerCase();
@@ -9223,14 +9325,14 @@ function slugify(value) {
9223
9325
  }
9224
9326
 
9225
9327
  // src/commands/memory-list.ts
9226
- import { existsSync as existsSync33 } from "fs";
9328
+ import { existsSync as existsSync34 } from "fs";
9227
9329
  import path17 from "path";
9228
9330
  import "commander";
9229
9331
  import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
9230
9332
 
9231
9333
  // src/utils/fs.ts
9232
9334
  import {
9233
- loadMemoriesFromDir as loadMemoriesFromDir25,
9335
+ loadMemoriesFromDir as loadMemoriesFromDir26,
9234
9336
  loadMemory,
9235
9337
  listMarkdownFilesRecursive
9236
9338
  } from "@hiveai/core";
@@ -9240,12 +9342,12 @@ function registerMemoryList(memory2) {
9240
9342
  memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("--limit <n>", "max memories to display").option("-d, --dir <dir>", "project root").action(async (opts) => {
9241
9343
  const root = findProjectRoot14(opts.dir);
9242
9344
  const paths = resolveHaivePaths11(root);
9243
- if (!existsSync33(paths.memoriesDir)) {
9345
+ if (!existsSync34(paths.memoriesDir)) {
9244
9346
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9245
9347
  process.exitCode = 1;
9246
9348
  return;
9247
9349
  }
9248
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
9350
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
9249
9351
  const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
9250
9352
  const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10)) : void 0;
9251
9353
  const filtered = all.filter((m) => {
@@ -9315,7 +9417,7 @@ function matchesFilters(loaded, opts) {
9315
9417
 
9316
9418
  // src/commands/memory-promote.ts
9317
9419
  import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
9318
- import { existsSync as existsSync34 } from "fs";
9420
+ import { existsSync as existsSync35 } from "fs";
9319
9421
  import path18 from "path";
9320
9422
  import "commander";
9321
9423
  import {
@@ -9328,12 +9430,12 @@ function registerMemoryPromote(memory2) {
9328
9430
  memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
9329
9431
  const root = findProjectRoot15(opts.dir);
9330
9432
  const paths = resolveHaivePaths12(root);
9331
- if (!existsSync34(paths.memoriesDir)) {
9433
+ if (!existsSync35(paths.memoriesDir)) {
9332
9434
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9333
9435
  process.exitCode = 1;
9334
9436
  return;
9335
9437
  }
9336
- const teamAndModule = await loadMemoriesFromDir25(paths.memoriesDir);
9438
+ const teamAndModule = await loadMemoriesFromDir26(paths.memoriesDir);
9337
9439
  const alreadyShared = teamAndModule.find(
9338
9440
  (m) => m.memory.frontmatter.id === id && (m.memory.frontmatter.scope === "team" || m.memory.frontmatter.scope === "module")
9339
9441
  );
@@ -9347,7 +9449,7 @@ function registerMemoryPromote(memory2) {
9347
9449
  }
9348
9450
  return;
9349
9451
  }
9350
- const all = await loadMemoriesFromDir25(paths.personalDir);
9452
+ const all = await loadMemoriesFromDir26(paths.personalDir);
9351
9453
  const found = all.find((m) => m.memory.frontmatter.id === id);
9352
9454
  if (!found) {
9353
9455
  ui.error(`No personal memory with id "${id}". (Promotion only applies to personal scope.)`);
@@ -9373,7 +9475,7 @@ function registerMemoryPromote(memory2) {
9373
9475
  }
9374
9476
 
9375
9477
  // src/commands/memory-approve.ts
9376
- import { existsSync as existsSync35 } from "fs";
9478
+ import { existsSync as existsSync36 } from "fs";
9377
9479
  import { writeFile as writeFile16 } from "fs/promises";
9378
9480
  import path19 from "path";
9379
9481
  import "commander";
@@ -9386,12 +9488,12 @@ function registerMemoryApprove(memory2) {
9386
9488
  memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
9387
9489
  const root = findProjectRoot16(opts.dir);
9388
9490
  const paths = resolveHaivePaths13(root);
9389
- if (!existsSync35(paths.memoriesDir)) {
9491
+ if (!existsSync36(paths.memoriesDir)) {
9390
9492
  ui.error(`No .ai/memories at ${root}.`);
9391
9493
  process.exitCode = 1;
9392
9494
  return;
9393
9495
  }
9394
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
9496
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
9395
9497
  if (opts.all || opts.pending) {
9396
9498
  const candidates = all.filter((m) => {
9397
9499
  const s = m.memory.frontmatter.status;
@@ -9445,7 +9547,7 @@ function registerMemoryApprove(memory2) {
9445
9547
 
9446
9548
  // src/commands/memory-update.ts
9447
9549
  import { readFile as readFile11, writeFile as writeFile17 } from "fs/promises";
9448
- import { existsSync as existsSync36 } from "fs";
9550
+ import { existsSync as existsSync37 } from "fs";
9449
9551
  import path20 from "path";
9450
9552
  import "commander";
9451
9553
  import {
@@ -9457,12 +9559,12 @@ function registerMemoryUpdate(memory2) {
9457
9559
  memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--type <type>", "change the memory type (convention | decision | gotcha | architecture | glossary | skill | attempt)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--body-file <path>", "read new body from a Markdown file \u2014 for long content").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
9458
9560
  const root = findProjectRoot17(opts.dir);
9459
9561
  const paths = resolveHaivePaths14(root);
9460
- if (!existsSync36(paths.memoriesDir)) {
9562
+ if (!existsSync37(paths.memoriesDir)) {
9461
9563
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9462
9564
  process.exitCode = 1;
9463
9565
  return;
9464
9566
  }
9465
- const memories = await loadMemoriesFromDir25(paths.memoriesDir);
9567
+ const memories = await loadMemoriesFromDir26(paths.memoriesDir);
9466
9568
  const loaded = memories.find((m) => m.memory.frontmatter.id === id);
9467
9569
  if (!loaded) {
9468
9570
  ui.error(`No memory with id "${id}".`);
@@ -9499,7 +9601,7 @@ function registerMemoryUpdate(memory2) {
9499
9601
  if (opts.author !== void 0) updated.push("author");
9500
9602
  let newBody;
9501
9603
  if (opts.bodyFile !== void 0) {
9502
- if (!existsSync36(opts.bodyFile)) {
9604
+ if (!existsSync37(opts.bodyFile)) {
9503
9605
  ui.error(`--body-file not found: ${opts.bodyFile}`);
9504
9606
  process.exitCode = 1;
9505
9607
  return;
@@ -9545,15 +9647,15 @@ function parseCsv3(value) {
9545
9647
 
9546
9648
  // src/commands/memory-auto-promote.ts
9547
9649
  import { writeFile as writeFile18 } from "fs/promises";
9548
- import { existsSync as existsSync37 } from "fs";
9650
+ import { existsSync as existsSync38 } from "fs";
9549
9651
  import path21 from "path";
9550
9652
  import "commander";
9551
9653
  import {
9552
9654
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
9553
9655
  findProjectRoot as findProjectRoot18,
9554
- getUsage as getUsage11,
9656
+ getUsage as getUsage12,
9555
9657
  isAutoPromoteEligible as isAutoPromoteEligible3,
9556
- loadUsageIndex as loadUsageIndex13,
9658
+ loadUsageIndex as loadUsageIndex14,
9557
9659
  resolveHaivePaths as resolveHaivePaths15,
9558
9660
  serializeMemory as serializeMemory16
9559
9661
  } from "@hiveai/core";
@@ -9565,7 +9667,7 @@ function registerMemoryAutoPromote(memory2) {
9565
9667
  ).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
9566
9668
  const root = findProjectRoot18(opts.dir);
9567
9669
  const paths = resolveHaivePaths15(root);
9568
- if (!existsSync37(paths.memoriesDir)) {
9670
+ if (!existsSync38(paths.memoriesDir)) {
9569
9671
  ui.error(`No .ai/memories at ${root}.`);
9570
9672
  process.exitCode = 1;
9571
9673
  return;
@@ -9574,10 +9676,10 @@ function registerMemoryAutoPromote(memory2) {
9574
9676
  minReads: Number(opts.minReads ?? DEFAULT_AUTO_PROMOTE_RULE3.minReads),
9575
9677
  maxRejections: Number(opts.maxRejections ?? DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
9576
9678
  };
9577
- const memories = await loadMemoriesFromDir25(paths.memoriesDir);
9578
- const usage = await loadUsageIndex13(paths);
9679
+ const memories = await loadMemoriesFromDir26(paths.memoriesDir);
9680
+ const usage = await loadUsageIndex14(paths);
9579
9681
  const eligible = memories.filter(
9580
- ({ memory: memory3 }) => isAutoPromoteEligible3(memory3.frontmatter, getUsage11(usage, memory3.frontmatter.id), rule)
9682
+ ({ memory: memory3 }) => isAutoPromoteEligible3(memory3.frontmatter, getUsage12(usage, memory3.frontmatter.id), rule)
9581
9683
  );
9582
9684
  if (eligible.length === 0) {
9583
9685
  ui.info(
@@ -9587,7 +9689,7 @@ function registerMemoryAutoPromote(memory2) {
9587
9689
  }
9588
9690
  let written = 0;
9589
9691
  for (const { memory: mem, filePath } of eligible) {
9590
- const u = getUsage11(usage, mem.frontmatter.id);
9692
+ const u = getUsage12(usage, mem.frontmatter.id);
9591
9693
  console.log(
9592
9694
  `${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
9593
9695
  );
@@ -9608,7 +9710,7 @@ function registerMemoryAutoPromote(memory2) {
9608
9710
 
9609
9711
  // src/commands/memory-edit.ts
9610
9712
  import { spawn as spawn3 } from "child_process";
9611
- import { existsSync as existsSync38 } from "fs";
9713
+ import { existsSync as existsSync39 } from "fs";
9612
9714
  import { readFile as readFile12 } from "fs/promises";
9613
9715
  import path23 from "path";
9614
9716
  import "commander";
@@ -9621,12 +9723,12 @@ function registerMemoryEdit(memory2) {
9621
9723
  memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
9622
9724
  const root = findProjectRoot19(opts.dir);
9623
9725
  const paths = resolveHaivePaths16(root);
9624
- if (!existsSync38(paths.memoriesDir)) {
9726
+ if (!existsSync39(paths.memoriesDir)) {
9625
9727
  ui.error(`No .ai/memories at ${root}.`);
9626
9728
  process.exitCode = 1;
9627
9729
  return;
9628
9730
  }
9629
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
9731
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
9630
9732
  const found = all.find((m) => m.memory.frontmatter.id === id);
9631
9733
  if (!found) {
9632
9734
  ui.error(`No memory with id "${id}".`);
@@ -9661,15 +9763,15 @@ function runEditor(editor, file) {
9661
9763
  }
9662
9764
 
9663
9765
  // src/commands/memory-for-files.ts
9664
- import { existsSync as existsSync39 } from "fs";
9766
+ import { existsSync as existsSync40 } from "fs";
9665
9767
  import path24 from "path";
9666
9768
  import "commander";
9667
9769
  import {
9668
9770
  deriveConfidence as deriveConfidence9,
9669
9771
  findProjectRoot as findProjectRoot20,
9670
- getUsage as getUsage12,
9772
+ getUsage as getUsage13,
9671
9773
  inferModulesFromPaths as inferModulesFromPaths4,
9672
- loadUsageIndex as loadUsageIndex14,
9774
+ loadUsageIndex as loadUsageIndex15,
9673
9775
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths5,
9674
9776
  resolveHaivePaths as resolveHaivePaths17
9675
9777
  } from "@hiveai/core";
@@ -9677,13 +9779,13 @@ function registerMemoryForFiles(memory2) {
9677
9779
  memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
9678
9780
  const root = findProjectRoot20(opts.dir);
9679
9781
  const paths = resolveHaivePaths17(root);
9680
- if (!existsSync39(paths.memoriesDir)) {
9782
+ if (!existsSync40(paths.memoriesDir)) {
9681
9783
  ui.error(`No .ai/memories at ${root}.`);
9682
9784
  process.exitCode = 1;
9683
9785
  return;
9684
9786
  }
9685
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
9686
- const usage = await loadUsageIndex14(paths);
9787
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
9788
+ const usage = await loadUsageIndex15(paths);
9687
9789
  const inferred = inferModulesFromPaths4(files);
9688
9790
  const byAnchor = [];
9689
9791
  const byModule = [];
@@ -9781,7 +9883,7 @@ function printGroup(root, label, loaded, usage) {
9781
9883
  \u2014 ${label} \u2014`));
9782
9884
  for (const { memory: mem, filePath } of loaded) {
9783
9885
  const fm = mem.frontmatter;
9784
- const u = getUsage12(usage, fm.id);
9886
+ const u = getUsage13(usage, fm.id);
9785
9887
  const conf = deriveConfidence9(fm, u);
9786
9888
  console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
9787
9889
  console.log(` ${ui.dim(path24.relative(root, filePath))}`);
@@ -9789,13 +9891,13 @@ function printGroup(root, label, loaded, usage) {
9789
9891
  }
9790
9892
 
9791
9893
  // src/commands/memory-hot.ts
9792
- import { existsSync as existsSync40 } from "fs";
9894
+ import { existsSync as existsSync41 } from "fs";
9793
9895
  import path25 from "path";
9794
9896
  import "commander";
9795
9897
  import {
9796
9898
  findProjectRoot as findProjectRoot21,
9797
- getUsage as getUsage13,
9798
- loadUsageIndex as loadUsageIndex15,
9899
+ getUsage as getUsage14,
9900
+ loadUsageIndex as loadUsageIndex16,
9799
9901
  resolveHaivePaths as resolveHaivePaths18
9800
9902
  } from "@hiveai/core";
9801
9903
  function registerMemoryHot(memory2) {
@@ -9804,23 +9906,23 @@ function registerMemoryHot(memory2) {
9804
9906
  ).option("--threshold <n>", "minimum read_count to qualify (default: 3)", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
9805
9907
  const root = findProjectRoot21(opts.dir);
9806
9908
  const paths = resolveHaivePaths18(root);
9807
- if (!existsSync40(paths.memoriesDir)) {
9909
+ if (!existsSync41(paths.memoriesDir)) {
9808
9910
  ui.error(`No .ai/memories at ${root}.`);
9809
9911
  process.exitCode = 1;
9810
9912
  return;
9811
9913
  }
9812
9914
  const threshold = Math.max(1, Number(opts.threshold ?? 3));
9813
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
9814
- const usage = await loadUsageIndex15(paths);
9915
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
9916
+ const usage = await loadUsageIndex16(paths);
9815
9917
  const candidates = all.filter(({ memory: mem }) => {
9816
9918
  const fm = mem.frontmatter;
9817
9919
  if (opts.status && fm.status !== opts.status) return false;
9818
9920
  if (opts.status === void 0 && fm.status !== "draft" && fm.status !== "proposed") {
9819
9921
  return false;
9820
9922
  }
9821
- return getUsage13(usage, fm.id).read_count >= threshold;
9923
+ return getUsage14(usage, fm.id).read_count >= threshold;
9822
9924
  }).sort(
9823
- (a, b) => getUsage13(usage, b.memory.frontmatter.id).read_count - getUsage13(usage, a.memory.frontmatter.id).read_count
9925
+ (a, b) => getUsage14(usage, b.memory.frontmatter.id).read_count - getUsage14(usage, a.memory.frontmatter.id).read_count
9824
9926
  );
9825
9927
  if (candidates.length === 0) {
9826
9928
  ui.info(`No hot memories (threshold=${threshold}).`);
@@ -9828,7 +9930,7 @@ function registerMemoryHot(memory2) {
9828
9930
  }
9829
9931
  for (const { memory: mem, filePath } of candidates) {
9830
9932
  const fm = mem.frontmatter;
9831
- const u = getUsage13(usage, fm.id);
9933
+ const u = getUsage14(usage, fm.id);
9832
9934
  console.log(
9833
9935
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(fm.status)} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
9834
9936
  );
@@ -9843,7 +9945,7 @@ function registerMemoryHot(memory2) {
9843
9945
 
9844
9946
  // src/commands/memory-tried.ts
9845
9947
  import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
9846
- import { existsSync as existsSync41 } from "fs";
9948
+ import { existsSync as existsSync43 } from "fs";
9847
9949
  import path26 from "path";
9848
9950
  import "commander";
9849
9951
  import {
@@ -9873,7 +9975,7 @@ function registerMemoryTried(memory2) {
9873
9975
  ).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) => {
9874
9976
  const root = findProjectRoot22(opts.dir);
9875
9977
  const paths = resolveHaivePaths19(root);
9876
- if (!existsSync41(paths.haiveDir)) {
9978
+ if (!existsSync43(paths.haiveDir)) {
9877
9979
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9878
9980
  process.exitCode = 1;
9879
9981
  return;
@@ -9901,7 +10003,7 @@ function registerMemoryTried(memory2) {
9901
10003
  }
9902
10004
  const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9903
10005
  await mkdir13(path26.dirname(file), { recursive: true });
9904
- if (existsSync41(file)) {
10006
+ if (existsSync43(file)) {
9905
10007
  ui.error(`Memory already exists at ${file}`);
9906
10008
  process.exitCode = 1;
9907
10009
  return;
@@ -9919,7 +10021,7 @@ function parseCsv4(value) {
9919
10021
 
9920
10022
  // src/commands/memory-seed.ts
9921
10023
  import { readFile as readFile13 } from "fs/promises";
9922
- import { existsSync as existsSync43 } from "fs";
10024
+ import { existsSync as existsSync44 } from "fs";
9923
10025
  import path27 from "path";
9924
10026
  import "commander";
9925
10027
  import {
@@ -9959,7 +10061,7 @@ function registerMemorySeed(memory2) {
9959
10061
  }
9960
10062
  return;
9961
10063
  }
9962
- if (!existsSync43(paths.haiveDir)) {
10064
+ if (!existsSync44(paths.haiveDir)) {
9963
10065
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9964
10066
  process.exitCode = 1;
9965
10067
  return;
@@ -10015,26 +10117,26 @@ function registerMemorySeed(memory2) {
10015
10117
  }
10016
10118
 
10017
10119
  // src/commands/memory-pending.ts
10018
- import { existsSync as existsSync44 } from "fs";
10120
+ import { existsSync as existsSync45 } from "fs";
10019
10121
  import path28 from "path";
10020
10122
  import "commander";
10021
10123
  import {
10022
10124
  findProjectRoot as findProjectRoot24,
10023
- getUsage as getUsage14,
10024
- loadUsageIndex as loadUsageIndex16,
10125
+ getUsage as getUsage15,
10126
+ loadUsageIndex as loadUsageIndex17,
10025
10127
  resolveHaivePaths as resolveHaivePaths21
10026
10128
  } from "@hiveai/core";
10027
10129
  function registerMemoryPending(memory2) {
10028
10130
  memory2.command("pending").description("List draft and proposed memories awaiting review (sorted by reads desc).\n\n draft = created but not yet activated \xB7 proposed = promoted, awaiting team validation").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
10029
10131
  const root = findProjectRoot24(opts.dir);
10030
10132
  const paths = resolveHaivePaths21(root);
10031
- if (!existsSync44(paths.memoriesDir)) {
10133
+ if (!existsSync45(paths.memoriesDir)) {
10032
10134
  ui.error(`No .ai/memories at ${root}.`);
10033
10135
  process.exitCode = 1;
10034
10136
  return;
10035
10137
  }
10036
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10037
- const usage = await loadUsageIndex16(paths);
10138
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10139
+ const usage = await loadUsageIndex17(paths);
10038
10140
  const filterFn = ({ memory: mem }) => {
10039
10141
  if (mem.frontmatter.status !== "proposed" && mem.frontmatter.status !== "draft") return false;
10040
10142
  if (opts.scope && mem.frontmatter.scope !== opts.scope) return false;
@@ -10047,7 +10149,7 @@ function registerMemoryPending(memory2) {
10047
10149
  return;
10048
10150
  }
10049
10151
  pending.sort(
10050
- (a, b) => getUsage14(usage, b.memory.frontmatter.id).read_count - getUsage14(usage, a.memory.frontmatter.id).read_count
10152
+ (a, b) => getUsage15(usage, b.memory.frontmatter.id).read_count - getUsage15(usage, a.memory.frontmatter.id).read_count
10051
10153
  );
10052
10154
  const now = Date.now();
10053
10155
  const drafts = pending.filter((m) => m.memory.frontmatter.status === "draft");
@@ -10056,7 +10158,7 @@ function registerMemoryPending(memory2) {
10056
10158
  console.log(ui.bold(`Proposed (${proposed.length}) \u2014 awaiting team validation`));
10057
10159
  for (const { memory: mem, filePath } of proposed) {
10058
10160
  const fm = mem.frontmatter;
10059
- const u = getUsage14(usage, fm.id);
10161
+ const u = getUsage15(usage, fm.id);
10060
10162
  const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
10061
10163
  const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
10062
10164
  console.log(
@@ -10071,7 +10173,7 @@ function registerMemoryPending(memory2) {
10071
10173
  console.log(ui.bold(`Draft (${drafts.length}) \u2014 created but not yet activated`));
10072
10174
  for (const { memory: mem, filePath } of drafts) {
10073
10175
  const fm = mem.frontmatter;
10074
- const u = getUsage14(usage, fm.id);
10176
+ const u = getUsage15(usage, fm.id);
10075
10177
  const ageDays = Math.floor((now - new Date(fm.created_at).getTime()) / 864e5);
10076
10178
  const ageStr = ageDays === 0 ? "today" : `${ageDays}d`;
10077
10179
  console.log(
@@ -10086,7 +10188,7 @@ function registerMemoryPending(memory2) {
10086
10188
  }
10087
10189
 
10088
10190
  // src/commands/memory-query.ts
10089
- import { existsSync as existsSync45 } from "fs";
10191
+ import { existsSync as existsSync46 } from "fs";
10090
10192
  import path29 from "path";
10091
10193
  import "commander";
10092
10194
  import {
@@ -10103,7 +10205,7 @@ function registerMemoryQuery(memory2) {
10103
10205
  memory2.command("query <text>").alias("search").description("Search memories by id, tag, or substring (AND, OR fallback). Alias: search").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
10104
10206
  const root = findProjectRoot25(opts.dir);
10105
10207
  const paths = resolveHaivePaths22(root);
10106
- if (!existsSync45(paths.memoriesDir)) {
10208
+ if (!existsSync46(paths.memoriesDir)) {
10107
10209
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
10108
10210
  process.exitCode = 1;
10109
10211
  return;
@@ -10114,7 +10216,7 @@ function registerMemoryQuery(memory2) {
10114
10216
  return;
10115
10217
  }
10116
10218
  const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
10117
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10219
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10118
10220
  const passesFilters2 = (mem) => {
10119
10221
  const fm = mem.frontmatter;
10120
10222
  if (opts.scope && fm.scope !== opts.scope) return false;
@@ -10162,26 +10264,26 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
10162
10264
 
10163
10265
  // src/commands/memory-reject.ts
10164
10266
  import { writeFile as writeFile20 } from "fs/promises";
10165
- import { existsSync as existsSync46 } from "fs";
10267
+ import { existsSync as existsSync47 } from "fs";
10166
10268
  import "commander";
10167
10269
  import {
10168
10270
  findProjectRoot as findProjectRoot26,
10169
- loadUsageIndex as loadUsageIndex17,
10170
- recordRejection as recordRejection2,
10271
+ loadUsageIndex as loadUsageIndex18,
10272
+ recordRejection as recordRejection3,
10171
10273
  resolveHaivePaths as resolveHaivePaths23,
10172
- saveUsageIndex as saveUsageIndex3,
10274
+ saveUsageIndex as saveUsageIndex4,
10173
10275
  serializeMemory as serializeMemory18
10174
10276
  } from "@hiveai/core";
10175
10277
  function registerMemoryReject(memory2) {
10176
10278
  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) => {
10177
10279
  const root = findProjectRoot26(opts.dir);
10178
10280
  const paths = resolveHaivePaths23(root);
10179
- if (!existsSync46(paths.memoriesDir)) {
10281
+ if (!existsSync47(paths.memoriesDir)) {
10180
10282
  ui.error(`No .ai/memories at ${root}.`);
10181
10283
  process.exitCode = 1;
10182
10284
  return;
10183
10285
  }
10184
- const memories = await loadMemoriesFromDir25(paths.memoriesDir);
10286
+ const memories = await loadMemoriesFromDir26(paths.memoriesDir);
10185
10287
  const loaded = memories.find((m) => m.memory.frontmatter.id === id);
10186
10288
  if (!loaded) {
10187
10289
  ui.error(`No memory with id "${id}".`);
@@ -10200,9 +10302,9 @@ function registerMemoryReject(memory2) {
10200
10302
  }),
10201
10303
  "utf8"
10202
10304
  );
10203
- const idx = await loadUsageIndex17(paths);
10204
- recordRejection2(idx, id, opts.reason ?? null);
10205
- await saveUsageIndex3(paths, idx);
10305
+ const idx = await loadUsageIndex18(paths);
10306
+ recordRejection3(idx, id, opts.reason ?? null);
10307
+ await saveUsageIndex4(paths, idx);
10206
10308
  const u = idx.by_id[id];
10207
10309
  ui.success(
10208
10310
  `Rejected ${id} (status=rejected, ${u.rejected_count} rejection${u.rejected_count === 1 ? "" : "s"})`
@@ -10212,27 +10314,27 @@ function registerMemoryReject(memory2) {
10212
10314
  }
10213
10315
 
10214
10316
  // src/commands/memory-rm.ts
10215
- import { existsSync as existsSync47 } from "fs";
10317
+ import { existsSync as existsSync48 } from "fs";
10216
10318
  import { unlink as unlink3 } from "fs/promises";
10217
10319
  import path30 from "path";
10218
10320
  import { createInterface as createInterface2 } from "readline/promises";
10219
10321
  import "commander";
10220
10322
  import {
10221
10323
  findProjectRoot as findProjectRoot27,
10222
- loadUsageIndex as loadUsageIndex18,
10324
+ loadUsageIndex as loadUsageIndex19,
10223
10325
  resolveHaivePaths as resolveHaivePaths24,
10224
- saveUsageIndex as saveUsageIndex4
10326
+ saveUsageIndex as saveUsageIndex5
10225
10327
  } from "@hiveai/core";
10226
10328
  function registerMemoryRm(memory2) {
10227
10329
  memory2.command("rm <id>").description("Delete a memory file (and its usage entry by default)").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
10228
10330
  const root = findProjectRoot27(opts.dir);
10229
10331
  const paths = resolveHaivePaths24(root);
10230
- if (!existsSync47(paths.memoriesDir)) {
10332
+ if (!existsSync48(paths.memoriesDir)) {
10231
10333
  ui.error(`No .ai/memories at ${root}.`);
10232
10334
  process.exitCode = 1;
10233
10335
  return;
10234
10336
  }
10235
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10337
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10236
10338
  const found = all.find((m) => m.memory.frontmatter.id === id);
10237
10339
  if (!found) {
10238
10340
  ui.error(`No memory with id "${id}".`);
@@ -10252,10 +10354,10 @@ function registerMemoryRm(memory2) {
10252
10354
  await unlink3(found.filePath);
10253
10355
  ui.success(`Deleted ${rel}`);
10254
10356
  if (!opts.keepUsage) {
10255
- const idx = await loadUsageIndex18(paths);
10357
+ const idx = await loadUsageIndex19(paths);
10256
10358
  if (idx.by_id[id]) {
10257
10359
  delete idx.by_id[id];
10258
- await saveUsageIndex4(paths, idx);
10360
+ await saveUsageIndex5(paths, idx);
10259
10361
  ui.info("Removed usage entry");
10260
10362
  }
10261
10363
  }
@@ -10263,27 +10365,27 @@ function registerMemoryRm(memory2) {
10263
10365
  }
10264
10366
 
10265
10367
  // src/commands/memory-show.ts
10266
- import { existsSync as existsSync48 } from "fs";
10368
+ import { existsSync as existsSync49 } from "fs";
10267
10369
  import { readFile as readFile14 } from "fs/promises";
10268
10370
  import path31 from "path";
10269
10371
  import "commander";
10270
10372
  import {
10271
10373
  deriveConfidence as deriveConfidence10,
10272
10374
  findProjectRoot as findProjectRoot28,
10273
- getUsage as getUsage15,
10274
- loadUsageIndex as loadUsageIndex19,
10375
+ getUsage as getUsage16,
10376
+ loadUsageIndex as loadUsageIndex20,
10275
10377
  resolveHaivePaths as resolveHaivePaths25
10276
10378
  } from "@hiveai/core";
10277
10379
  function registerMemoryShow(memory2) {
10278
10380
  memory2.command("show <id>").description("Print a memory's frontmatter, body, and confidence/usage").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
10279
10381
  const root = findProjectRoot28(opts.dir);
10280
10382
  const paths = resolveHaivePaths25(root);
10281
- if (!existsSync48(paths.memoriesDir)) {
10383
+ if (!existsSync49(paths.memoriesDir)) {
10282
10384
  ui.error(`No .ai/memories at ${root}.`);
10283
10385
  process.exitCode = 1;
10284
10386
  return;
10285
10387
  }
10286
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10388
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10287
10389
  const found = all.find((m) => m.memory.frontmatter.id === id);
10288
10390
  if (!found) {
10289
10391
  ui.error(`No memory with id "${id}".`);
@@ -10295,8 +10397,8 @@ function registerMemoryShow(memory2) {
10295
10397
  return;
10296
10398
  }
10297
10399
  const fm = found.memory.frontmatter;
10298
- const usage = await loadUsageIndex19(paths);
10299
- const u = getUsage15(usage, fm.id);
10400
+ const usage = await loadUsageIndex20(paths);
10401
+ const u = getUsage16(usage, fm.id);
10300
10402
  const conf = deriveConfidence10(fm, u);
10301
10403
  console.log(ui.bold(fm.id));
10302
10404
  console.log(`${ui.dim("scope:")} ${fm.scope}${fm.module ? ` / ${fm.module}` : ""}`);
@@ -10322,38 +10424,38 @@ function registerMemoryShow(memory2) {
10322
10424
  }
10323
10425
 
10324
10426
  // src/commands/memory-stats.ts
10325
- import { existsSync as existsSync49 } from "fs";
10427
+ import { existsSync as existsSync50 } from "fs";
10326
10428
  import path33 from "path";
10327
10429
  import "commander";
10328
10430
  import {
10329
10431
  deriveConfidence as deriveConfidence11,
10330
10432
  findProjectRoot as findProjectRoot29,
10331
- getUsage as getUsage16,
10332
- loadUsageIndex as loadUsageIndex20,
10433
+ getUsage as getUsage17,
10434
+ loadUsageIndex as loadUsageIndex21,
10333
10435
  resolveHaivePaths as resolveHaivePaths26
10334
10436
  } from "@hiveai/core";
10335
10437
  function registerMemoryStats(memory2) {
10336
10438
  memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
10337
10439
  const root = findProjectRoot29(opts.dir);
10338
10440
  const paths = resolveHaivePaths26(root);
10339
- if (!existsSync49(paths.memoriesDir)) {
10441
+ if (!existsSync50(paths.memoriesDir)) {
10340
10442
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10341
10443
  process.exitCode = 1;
10342
10444
  return;
10343
10445
  }
10344
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10345
- const usage = await loadUsageIndex20(paths);
10446
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10447
+ const usage = await loadUsageIndex21(paths);
10346
10448
  const target = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
10347
10449
  if (target.length === 0) {
10348
10450
  ui.info(opts.id ? `No memory with id "${opts.id}".` : "No memories.");
10349
10451
  return;
10350
10452
  }
10351
10453
  target.sort(
10352
- (a, b) => getUsage16(usage, b.memory.frontmatter.id).read_count - getUsage16(usage, a.memory.frontmatter.id).read_count
10454
+ (a, b) => getUsage17(usage, b.memory.frontmatter.id).read_count - getUsage17(usage, a.memory.frontmatter.id).read_count
10353
10455
  );
10354
10456
  for (const { memory: mem, filePath } of target) {
10355
10457
  const fm = mem.frontmatter;
10356
- const u = getUsage16(usage, fm.id);
10458
+ const u = getUsage17(usage, fm.id);
10357
10459
  const conf = deriveConfidence11(fm, u);
10358
10460
  console.log(
10359
10461
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`
@@ -10366,14 +10468,102 @@ function registerMemoryStats(memory2) {
10366
10468
  });
10367
10469
  }
10368
10470
 
10471
+ // src/commands/memory-impact.ts
10472
+ import { existsSync as existsSync51 } from "fs";
10473
+ import "commander";
10474
+ import {
10475
+ compareImpact,
10476
+ computeImpact as computeImpact3,
10477
+ findProjectRoot as findProjectRoot30,
10478
+ getUsage as getUsage18,
10479
+ loadUsageIndex as loadUsageIndex23,
10480
+ resolveHaivePaths as resolveHaivePaths27,
10481
+ summarizeImpact
10482
+ } from "@hiveai/core";
10483
+ function registerMemoryImpact(memory2) {
10484
+ memory2.command("impact").description(
10485
+ "Score memories by demonstrated utility (reads + applied outcomes + sensor fires vs rejections, staleness, dormancy) and surface prune candidates."
10486
+ ).option("--id <id>", "show impact for a single memory id").option("--prune", "list only prune candidates (dead weight worth reviewing)", false).option("--tier <tier>", "filter to a tier: high | medium | low | dormant").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
10487
+ const root = findProjectRoot30(opts.dir);
10488
+ const paths = resolveHaivePaths27(root);
10489
+ if (!existsSync51(paths.memoriesDir)) {
10490
+ ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10491
+ process.exitCode = 1;
10492
+ return;
10493
+ }
10494
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10495
+ const usageIndex = await loadUsageIndex23(paths);
10496
+ let rows = all.filter((m) => !opts.id || m.memory.frontmatter.id === opts.id).map(({ memory: mem }) => {
10497
+ const fm = mem.frontmatter;
10498
+ return {
10499
+ id: fm.id,
10500
+ type: fm.type,
10501
+ scope: fm.scope,
10502
+ status: fm.status,
10503
+ impact: computeImpact3(fm, getUsage18(usageIndex, fm.id))
10504
+ };
10505
+ });
10506
+ if (opts.prune) rows = rows.filter((r) => r.impact.pruneCandidate);
10507
+ if (opts.tier) {
10508
+ const tier = opts.tier;
10509
+ rows = rows.filter((r) => r.impact.tier === tier);
10510
+ }
10511
+ rows.sort((a, b) => compareImpact(a.impact, b.impact));
10512
+ const summary = summarizeImpact(all.map((m) => computeImpact3(m.memory.frontmatter, getUsage18(usageIndex, m.memory.frontmatter.id))));
10513
+ if (opts.json) {
10514
+ console.log(JSON.stringify({ root, summary, rows }, null, 2));
10515
+ return;
10516
+ }
10517
+ if (rows.length === 0) {
10518
+ ui.info(opts.prune ? "No prune candidates \u2014 every memory earns its keep." : "No memories matched.");
10519
+ return;
10520
+ }
10521
+ console.log(ui.bold(`hAIve memory impact \u2014 ${root}`));
10522
+ console.log(
10523
+ ui.dim(
10524
+ `${summary.total} memories \xB7 ${summary.high} high \xB7 ${summary.medium} medium \xB7 ${summary.low} low \xB7 ${summary.dormant} dormant \xB7 ${summary.prune_candidates} prune candidates`
10525
+ )
10526
+ );
10527
+ console.log();
10528
+ console.log(`${"score".padStart(5)} ${"tier".padEnd(7)} ${pad("id", 52)} ${"prune".padEnd(5)} signals`);
10529
+ console.log("\u2500".repeat(108));
10530
+ for (const r of rows) {
10531
+ const score = r.impact.score.toFixed(2).padStart(5);
10532
+ const tier = tierBadge(r.impact.tier).padEnd(7);
10533
+ const prune = r.impact.pruneCandidate ? ui.yellow("prune") : " ";
10534
+ console.log(`${score} ${tier} ${pad(r.id, 52)} ${prune} ${ui.dim(r.impact.signals.join(", ") || "no signals")}`);
10535
+ }
10536
+ if (!opts.prune && summary.prune_candidates > 0) {
10537
+ console.log();
10538
+ console.log(ui.dim(`Tip: \`haive memory impact --prune\` lists the ${summary.prune_candidates} prune candidate(s).`));
10539
+ }
10540
+ });
10541
+ }
10542
+ function tierBadge(tier) {
10543
+ switch (tier) {
10544
+ case "high":
10545
+ return ui.green(tier);
10546
+ case "medium":
10547
+ return ui.yellow(tier);
10548
+ case "dormant":
10549
+ return ui.dim(tier);
10550
+ default:
10551
+ return tier;
10552
+ }
10553
+ }
10554
+ function pad(value, width) {
10555
+ if (value.length <= width) return value.padEnd(width);
10556
+ return value.slice(0, width - 1) + "\u2026";
10557
+ }
10558
+
10369
10559
  // src/commands/memory-verify.ts
10370
10560
  import { writeFile as writeFile21 } from "fs/promises";
10371
- import { existsSync as existsSync50 } from "fs";
10561
+ import { existsSync as existsSync53 } from "fs";
10372
10562
  import path34 from "path";
10373
10563
  import "commander";
10374
10564
  import {
10375
- findProjectRoot as findProjectRoot30,
10376
- resolveHaivePaths as resolveHaivePaths27,
10565
+ findProjectRoot as findProjectRoot31,
10566
+ resolveHaivePaths as resolveHaivePaths28,
10377
10567
  serializeMemory as serializeMemory19,
10378
10568
  verifyAnchor as verifyAnchor3
10379
10569
  } from "@hiveai/core";
@@ -10381,9 +10571,9 @@ function registerMemoryVerify(memory2) {
10381
10571
  memory2.command("verify").description(
10382
10572
  "Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n haive sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'haive memory verify' to your haive-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n haive memory verify # check all, report only\n haive memory verify --update # mark stale/fresh on disk\n haive memory verify --id 2026-04-28-gotcha-x # check one memory\n"
10383
10573
  ).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
10384
- const root = findProjectRoot30(opts.dir);
10385
- const paths = resolveHaivePaths27(root);
10386
- if (!existsSync50(paths.memoriesDir)) {
10574
+ const root = findProjectRoot31(opts.dir);
10575
+ const paths = resolveHaivePaths28(root);
10576
+ if (!existsSync53(paths.memoriesDir)) {
10387
10577
  if (opts.json) {
10388
10578
  console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
10389
10579
  } else {
@@ -10392,7 +10582,7 @@ function registerMemoryVerify(memory2) {
10392
10582
  process.exitCode = 1;
10393
10583
  return;
10394
10584
  }
10395
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
10585
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
10396
10586
  const targets = opts.id ? all.filter((m) => m.memory.frontmatter.id === opts.id) : all;
10397
10587
  if (opts.id && targets.length === 0) {
10398
10588
  if (opts.json) {
@@ -10504,24 +10694,24 @@ function applyVerification2(mem, result) {
10504
10694
 
10505
10695
  // src/commands/memory-import.ts
10506
10696
  import { readFile as readFile15 } from "fs/promises";
10507
- import { existsSync as existsSync51 } from "fs";
10697
+ import { existsSync as existsSync54 } from "fs";
10508
10698
  import "commander";
10509
10699
  import {
10510
- findProjectRoot as findProjectRoot31,
10511
- resolveHaivePaths as resolveHaivePaths28
10700
+ findProjectRoot as findProjectRoot32,
10701
+ resolveHaivePaths as resolveHaivePaths29
10512
10702
  } from "@hiveai/core";
10513
10703
  function registerMemoryImport(memory2) {
10514
10704
  memory2.command("import").description(
10515
10705
  "Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
10516
10706
  ).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
10517
- const root = findProjectRoot31(opts.dir);
10518
- const paths = resolveHaivePaths28(root);
10519
- if (!existsSync51(paths.haiveDir)) {
10707
+ const root = findProjectRoot32(opts.dir);
10708
+ const paths = resolveHaivePaths29(root);
10709
+ if (!existsSync54(paths.haiveDir)) {
10520
10710
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10521
10711
  process.exitCode = 1;
10522
10712
  return;
10523
10713
  }
10524
- if (!existsSync51(opts.from)) {
10714
+ if (!existsSync54(opts.from)) {
10525
10715
  ui.error(`File not found: ${opts.from}`);
10526
10716
  process.exitCode = 1;
10527
10717
  return;
@@ -10554,14 +10744,14 @@ function registerMemoryImport(memory2) {
10554
10744
  }
10555
10745
 
10556
10746
  // src/commands/memory-import-changelog.ts
10557
- import { existsSync as existsSync53 } from "fs";
10747
+ import { existsSync as existsSync55 } from "fs";
10558
10748
  import { readFile as readFile16, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
10559
10749
  import path35 from "path";
10560
10750
  import "commander";
10561
10751
  import {
10562
10752
  buildFrontmatter as buildFrontmatter9,
10563
- findProjectRoot as findProjectRoot32,
10564
- resolveHaivePaths as resolveHaivePaths29,
10753
+ findProjectRoot as findProjectRoot33,
10754
+ resolveHaivePaths as resolveHaivePaths30,
10565
10755
  serializeMemory as serializeMemory20
10566
10756
  } from "@hiveai/core";
10567
10757
  function parseChangelog(content) {
@@ -10626,10 +10816,10 @@ function registerMemoryImportChangelog(memory2) {
10626
10816
  "--versions <csv>",
10627
10817
  "only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
10628
10818
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
10629
- const root = findProjectRoot32(opts.dir);
10630
- const paths = resolveHaivePaths29(root);
10819
+ const root = findProjectRoot33(opts.dir);
10820
+ const paths = resolveHaivePaths30(root);
10631
10821
  const changelogPath = path35.resolve(root, opts.fromChangelog);
10632
- if (!existsSync53(changelogPath)) {
10822
+ if (!existsSync55(changelogPath)) {
10633
10823
  ui.error(`CHANGELOG not found: ${changelogPath}`);
10634
10824
  process.exitCode = 1;
10635
10825
  return;
@@ -10716,17 +10906,17 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
10716
10906
  }
10717
10907
 
10718
10908
  // src/commands/memory-digest.ts
10719
- import { existsSync as existsSync54 } from "fs";
10909
+ import { existsSync as existsSync56 } from "fs";
10720
10910
  import { writeFile as writeFile24 } from "fs/promises";
10721
10911
  import path36 from "path";
10722
10912
  import "commander";
10723
10913
  import {
10724
10914
  deriveConfidence as deriveConfidence12,
10725
- findProjectRoot as findProjectRoot33,
10726
- getUsage as getUsage17,
10727
- loadMemoriesFromDir as loadMemoriesFromDir26,
10728
- loadUsageIndex as loadUsageIndex21,
10729
- resolveHaivePaths as resolveHaivePaths30
10915
+ findProjectRoot as findProjectRoot34,
10916
+ getUsage as getUsage19,
10917
+ loadMemoriesFromDir as loadMemoriesFromDir27,
10918
+ loadUsageIndex as loadUsageIndex24,
10919
+ resolveHaivePaths as resolveHaivePaths31
10730
10920
  } from "@hiveai/core";
10731
10921
  var CONFIDENCE_EMOJI = {
10732
10922
  unverified: "\u2B1C",
@@ -10739,9 +10929,9 @@ function registerMemoryDigest(program2) {
10739
10929
  program2.command("digest").description(
10740
10930
  "Generate a Markdown review digest of recently added or updated memories.\n\n Groups memories by type, shows confidence, status, read count, and anchor info.\n Each memory has action checkboxes (approve / reject / keep as-is) for peer review.\n\n Use this to do a bulk weekly review of team memories, or share with teammates\n as a pull-request attachment so humans can validate what the AI captured.\n\n Examples:\n haive memory digest # last 7 days, team scope\n haive memory digest --days 30 --scope all # last 30 days, all scopes\n haive memory digest --out review.md # write to file\n"
10741
10931
  ).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
10742
- const root = findProjectRoot33(opts.dir);
10743
- const paths = resolveHaivePaths30(root);
10744
- if (!existsSync54(paths.memoriesDir)) {
10932
+ const root = findProjectRoot34(opts.dir);
10933
+ const paths = resolveHaivePaths31(root);
10934
+ if (!existsSync56(paths.memoriesDir)) {
10745
10935
  ui.error("No .ai/memories found. Run `haive init` first.");
10746
10936
  process.exitCode = 1;
10747
10937
  return;
@@ -10749,8 +10939,8 @@ function registerMemoryDigest(program2) {
10749
10939
  const days = Math.max(1, Number(opts.days ?? 7));
10750
10940
  const scopeFilter = opts.scope ?? "team";
10751
10941
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
10752
- const all = await loadMemoriesFromDir26(paths.memoriesDir);
10753
- const usage = await loadUsageIndex21(paths);
10942
+ const all = await loadMemoriesFromDir27(paths.memoriesDir);
10943
+ const usage = await loadUsageIndex24(paths);
10754
10944
  const recent = all.filter(({ memory: mem }) => {
10755
10945
  const fm = mem.frontmatter;
10756
10946
  if (fm.type === "session_recap") return false;
@@ -10781,7 +10971,7 @@ function registerMemoryDigest(program2) {
10781
10971
  lines.push(``);
10782
10972
  for (const { memory: mem } of mems) {
10783
10973
  const fm = mem.frontmatter;
10784
- const u = getUsage17(usage, fm.id);
10974
+ const u = getUsage19(usage, fm.id);
10785
10975
  const confidence = deriveConfidence12(fm, u);
10786
10976
  const emoji = CONFIDENCE_EMOJI[confidence] ?? "\u2B1C";
10787
10977
  const anchor = fm.anchor.paths.length > 0 ? `\`${fm.anchor.paths[0]}\`` + (fm.anchor.paths.length > 1 ? ` +${fm.anchor.paths.length - 1}` : "") : "_no anchor_";
@@ -10824,21 +11014,21 @@ function registerMemoryDigest(program2) {
10824
11014
 
10825
11015
  // src/commands/session-end.ts
10826
11016
  import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile17, rm as rm2 } from "fs/promises";
10827
- import { existsSync as existsSync55 } from "fs";
11017
+ import { existsSync as existsSync57 } from "fs";
10828
11018
  import { spawn as spawn4 } from "child_process";
10829
11019
  import path37 from "path";
10830
11020
  import "commander";
10831
11021
  import {
10832
11022
  buildFrontmatter as buildFrontmatter10,
10833
- findProjectRoot as findProjectRoot34,
10834
- loadMemoriesFromDir as loadMemoriesFromDir27,
11023
+ findProjectRoot as findProjectRoot35,
11024
+ loadMemoriesFromDir as loadMemoriesFromDir28,
10835
11025
  memoryFilePath as memoryFilePath9,
10836
- resolveHaivePaths as resolveHaivePaths31,
11026
+ resolveHaivePaths as resolveHaivePaths32,
10837
11027
  serializeMemory as serializeMemory21
10838
11028
  } from "@hiveai/core";
10839
11029
  async function buildAutoRecap(paths) {
10840
11030
  const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
10841
- if (!existsSync55(obsFile)) return await buildGitAutoRecap(paths);
11031
+ if (!existsSync57(obsFile)) return await buildGitAutoRecap(paths);
10842
11032
  const raw = await readFile17(obsFile, "utf8").catch(() => "");
10843
11033
  if (!raw.trim()) return await buildGitAutoRecap(paths);
10844
11034
  const lines = raw.split("\n").filter(Boolean);
@@ -11027,9 +11217,9 @@ function registerSessionEnd(session2) {
11027
11217
  --next "Add integration tests for webhook signature validation"
11028
11218
  `
11029
11219
  ).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
11030
- const root = findProjectRoot34(opts.dir);
11031
- const paths = resolveHaivePaths31(root);
11032
- if (!existsSync55(paths.haiveDir)) {
11220
+ const root = findProjectRoot35(opts.dir);
11221
+ const paths = resolveHaivePaths32(root);
11222
+ if (!existsSync57(paths.haiveDir)) {
11033
11223
  if (opts.auto || opts.quiet) return;
11034
11224
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11035
11225
  process.exitCode = 1;
@@ -11062,7 +11252,7 @@ function registerSessionEnd(session2) {
11062
11252
  });
11063
11253
  const topic = recapTopic2(scope, opts.module);
11064
11254
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
11065
- const missingPaths = filesTouched.filter((p) => !existsSync55(path37.resolve(root, p)));
11255
+ const missingPaths = filesTouched.filter((p) => !existsSync57(path37.resolve(root, p)));
11066
11256
  if (missingPaths.length > 0 && !opts.quiet) {
11067
11257
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
11068
11258
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
@@ -11070,11 +11260,11 @@ function registerSessionEnd(session2) {
11070
11260
  const cleanupObservations = async () => {
11071
11261
  if (!opts.auto) return;
11072
11262
  const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
11073
- if (existsSync55(obsFile)) await rm2(obsFile).catch(() => {
11263
+ if (existsSync57(obsFile)) await rm2(obsFile).catch(() => {
11074
11264
  });
11075
11265
  };
11076
- if (existsSync55(paths.memoriesDir)) {
11077
- const existing = await loadMemoriesFromDir27(paths.memoriesDir);
11266
+ if (existsSync57(paths.memoriesDir)) {
11267
+ const existing = await loadMemoriesFromDir28(paths.memoriesDir);
11078
11268
  const topicMatch = existing.find(
11079
11269
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
11080
11270
  );
@@ -11135,15 +11325,15 @@ function normalizeAnchorPath(root, filePath) {
11135
11325
  }
11136
11326
 
11137
11327
  // src/commands/snapshot.ts
11138
- import { existsSync as existsSync56 } from "fs";
11328
+ import { existsSync as existsSync58 } from "fs";
11139
11329
  import { readdir as readdir4 } from "fs/promises";
11140
11330
  import path38 from "path";
11141
11331
  import "commander";
11142
11332
  import {
11143
11333
  diffContract,
11144
- findProjectRoot as findProjectRoot35,
11334
+ findProjectRoot as findProjectRoot36,
11145
11335
  loadConfig as loadConfig7,
11146
- resolveHaivePaths as resolveHaivePaths32,
11336
+ resolveHaivePaths as resolveHaivePaths33,
11147
11337
  snapshotContract
11148
11338
  } from "@hiveai/core";
11149
11339
  function registerSnapshot(program2) {
@@ -11168,16 +11358,16 @@ function registerSnapshot(program2) {
11168
11358
  "--format <format>",
11169
11359
  "contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
11170
11360
  ).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
11171
- const root = findProjectRoot35(opts.dir);
11172
- const paths = resolveHaivePaths32(root);
11173
- if (!existsSync56(paths.haiveDir)) {
11361
+ const root = findProjectRoot36(opts.dir);
11362
+ const paths = resolveHaivePaths33(root);
11363
+ if (!existsSync58(paths.haiveDir)) {
11174
11364
  ui.error("No .ai/ found. Run `haive init` first.");
11175
11365
  process.exitCode = 1;
11176
11366
  return;
11177
11367
  }
11178
11368
  if (opts.list) {
11179
11369
  const contractsDir = path38.join(paths.haiveDir, "contracts");
11180
- if (!existsSync56(contractsDir)) {
11370
+ if (!existsSync58(contractsDir)) {
11181
11371
  console.log(ui.dim("No contract snapshots found."));
11182
11372
  return;
11183
11373
  }
@@ -11301,16 +11491,16 @@ function detectFormat(filePath) {
11301
11491
  }
11302
11492
 
11303
11493
  // src/commands/hub.ts
11304
- import { existsSync as existsSync57 } from "fs";
11494
+ import { existsSync as existsSync59 } from "fs";
11305
11495
  import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile26, copyFile } from "fs/promises";
11306
11496
  import path39 from "path";
11307
11497
  import { spawnSync as spawnSync5 } from "child_process";
11308
11498
  import "commander";
11309
11499
  import {
11310
- findProjectRoot as findProjectRoot36,
11500
+ findProjectRoot as findProjectRoot37,
11311
11501
  loadConfig as loadConfig8,
11312
- loadMemoriesFromDir as loadMemoriesFromDir28,
11313
- resolveHaivePaths as resolveHaivePaths33,
11502
+ loadMemoriesFromDir as loadMemoriesFromDir29,
11503
+ resolveHaivePaths as resolveHaivePaths34,
11314
11504
  saveConfig as saveConfig3,
11315
11505
  serializeMemory as serializeMemory23
11316
11506
  } from "@hiveai/core";
@@ -11392,8 +11582,8 @@ Next steps:
11392
11582
  haive hub push --commit --message "feat: add payment API contract memories"
11393
11583
  `
11394
11584
  ).option("-d, --dir <dir>", "project root").option("--commit", "auto-commit to the hub repo after pushing").option("--message <msg>", "commit message for the hub (used with --commit)").action(async (opts) => {
11395
- const root = findProjectRoot36(opts.dir);
11396
- const paths = resolveHaivePaths33(root);
11585
+ const root = findProjectRoot37(opts.dir);
11586
+ const paths = resolveHaivePaths34(root);
11397
11587
  const config = await loadConfig8(paths);
11398
11588
  if (!config.hubPath) {
11399
11589
  ui.error(
@@ -11403,7 +11593,7 @@ Next steps:
11403
11593
  return;
11404
11594
  }
11405
11595
  const hubRoot = path39.resolve(root, config.hubPath);
11406
- if (!existsSync57(hubRoot)) {
11596
+ if (!existsSync59(hubRoot)) {
11407
11597
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
11408
11598
  process.exitCode = 1;
11409
11599
  return;
@@ -11411,7 +11601,7 @@ Next steps:
11411
11601
  const projectName = path39.basename(root);
11412
11602
  const destDir = path39.join(hubRoot, ".ai", "memories", "shared", projectName);
11413
11603
  await mkdir16(destDir, { recursive: true });
11414
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11604
+ const all = await loadMemoriesFromDir29(paths.memoriesDir);
11415
11605
  const shared = all.filter(
11416
11606
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
11417
11607
  !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
@@ -11461,8 +11651,8 @@ Next steps:
11461
11651
  hub.command("pull").description(
11462
11652
  "Pull shared memories from the hub into this project.\n\n Imports all memories from hub/.ai/memories/shared/ EXCEPT this project's own.\n Imported memories land in .ai/memories/shared/<source-project-name>/.\n\n Examples:\n haive hub pull\n"
11463
11653
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
11464
- const root = findProjectRoot36(opts.dir);
11465
- const paths = resolveHaivePaths33(root);
11654
+ const root = findProjectRoot37(opts.dir);
11655
+ const paths = resolveHaivePaths34(root);
11466
11656
  const config = await loadConfig8(paths);
11467
11657
  if (!config.hubPath) {
11468
11658
  ui.error(
@@ -11473,7 +11663,7 @@ Next steps:
11473
11663
  }
11474
11664
  const hubRoot = path39.resolve(root, config.hubPath);
11475
11665
  const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
11476
- if (!existsSync57(hubSharedDir)) {
11666
+ if (!existsSync59(hubSharedDir)) {
11477
11667
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
11478
11668
  return;
11479
11669
  }
@@ -11520,15 +11710,15 @@ Next steps:
11520
11710
  );
11521
11711
  });
11522
11712
  hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
11523
- const root = findProjectRoot36(opts.dir);
11524
- const paths = resolveHaivePaths33(root);
11713
+ const root = findProjectRoot37(opts.dir);
11714
+ const paths = resolveHaivePaths34(root);
11525
11715
  const config = await loadConfig8(paths);
11526
11716
  console.log(ui.bold("Hub status"));
11527
11717
  console.log(
11528
11718
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
11529
11719
  );
11530
11720
  const sharedDir = path39.join(paths.memoriesDir, "shared");
11531
- if (existsSync57(sharedDir)) {
11721
+ if (existsSync59(sharedDir)) {
11532
11722
  const { readdir: readdir7 } = await import("fs/promises");
11533
11723
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
11534
11724
  console.log(`
@@ -11540,7 +11730,7 @@ Next steps:
11540
11730
  } else {
11541
11731
  console.log(ui.dim(" No imported shared memories yet."));
11542
11732
  }
11543
- const all = await loadMemoriesFromDir28(paths.memoriesDir);
11733
+ const all = await loadMemoriesFromDir29(paths.memoriesDir);
11544
11734
  const outgoing = all.filter(
11545
11735
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && !memory2.frontmatter.tags.some((t) => t.startsWith("cross-repo:"))
11546
11736
  );
@@ -11557,17 +11747,17 @@ Next steps:
11557
11747
 
11558
11748
  // src/commands/stats.ts
11559
11749
  import "commander";
11560
- import { existsSync as existsSync58 } from "fs";
11750
+ import { existsSync as existsSync60 } from "fs";
11561
11751
  import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
11562
11752
  import path40 from "path";
11563
11753
  import {
11564
11754
  aggregateUsage,
11565
- findProjectRoot as findProjectRoot37,
11566
- loadMemoriesFromDir as loadMemoriesFromDir29,
11567
- loadUsageIndex as loadUsageIndex23,
11755
+ findProjectRoot as findProjectRoot38,
11756
+ loadMemoriesFromDir as loadMemoriesFromDir30,
11757
+ loadUsageIndex as loadUsageIndex25,
11568
11758
  parseSince,
11569
11759
  readUsageEvents as readUsageEvents2,
11570
- resolveHaivePaths as resolveHaivePaths34,
11760
+ resolveHaivePaths as resolveHaivePaths35,
11571
11761
  usageLogSize
11572
11762
  } from "@hiveai/core";
11573
11763
  function registerStats(program2) {
@@ -11576,8 +11766,8 @@ function registerStats(program2) {
11576
11766
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
11577
11767
  void 0
11578
11768
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
11579
- const root = findProjectRoot37(opts.dir);
11580
- const paths = resolveHaivePaths34(root);
11769
+ const root = findProjectRoot38(opts.dir);
11770
+ const paths = resolveHaivePaths35(root);
11581
11771
  if (opts.exportReport) {
11582
11772
  await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
11583
11773
  return;
@@ -11623,9 +11813,9 @@ function registerStats(program2) {
11623
11813
  const maxCount = aggregate.by_tool[0]?.count ?? 1;
11624
11814
  for (const t of aggregate.by_tool.slice(0, 20)) {
11625
11815
  const bar = "\u2588".repeat(Math.max(1, Math.round(t.count / maxCount * 30)));
11626
- const pct = (t.count / aggregate.total * 100).toFixed(1);
11816
+ const pct2 = (t.count / aggregate.total * 100).toFixed(1);
11627
11817
  console.log(
11628
- ` ${t.tool.padEnd(28)} ${ui.green(bar)} ${ui.bold(String(t.count))} ${ui.dim(`(${pct}%, last ${t.last_used.slice(0, 19)})`)}`
11818
+ ` ${t.tool.padEnd(28)} ${ui.green(bar)} ${ui.bold(String(t.count))} ${ui.dim(`(${pct2}%, last ${t.last_used.slice(0, 19)})`)}`
11629
11819
  );
11630
11820
  }
11631
11821
  });
@@ -11635,8 +11825,8 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11635
11825
  const size = await usageLogSize(paths);
11636
11826
  let events = await readUsageEvents2(paths);
11637
11827
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
11638
- if (existsSync58(paths.memoriesDir)) {
11639
- const mems = await loadMemoriesFromDir29(paths.memoriesDir);
11828
+ if (existsSync60(paths.memoriesDir)) {
11829
+ const mems = await loadMemoriesFromDir30(paths.memoriesDir);
11640
11830
  for (const { memory: memory2 } of mems) {
11641
11831
  const fm = memory2.frontmatter;
11642
11832
  if (fm.type === "session_recap") memoryCount.total_skipped_session++;
@@ -11650,7 +11840,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11650
11840
  const briefingCalls = events.filter((e) => inWindow(e.at) && e.tool === "get_briefing").length;
11651
11841
  let memoryHitsLeader = null;
11652
11842
  try {
11653
- const usageIdx = await loadUsageIndex23(paths);
11843
+ const usageIdx = await loadUsageIndex25(paths);
11654
11844
  const tops = Object.entries(usageIdx.by_id).map(([id, v]) => ({ id, read_count: v.read_count })).filter((x) => x.read_count > 0).sort((a, b) => b.read_count - a.read_count);
11655
11845
  memoryHitsLeader = tops[0] ?? null;
11656
11846
  } catch {
@@ -11682,7 +11872,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11682
11872
  ui.success(`Wrote ROI / usage rollup \u2192 ${outAbs}`);
11683
11873
  }
11684
11874
  async function renderMemoryHits(paths, opts) {
11685
- const index = await loadUsageIndex23(paths);
11875
+ const index = await loadUsageIndex25(paths);
11686
11876
  const since = parseSince(opts.since ?? "30d");
11687
11877
  const sinceMs = since ? new Date(since).getTime() : null;
11688
11878
  const entries = Object.entries(index.by_id).map(([id, usage]) => ({ id, ...usage })).filter((e) => e.read_count > 0).filter((e) => {
@@ -11730,13 +11920,13 @@ import { performance } from "perf_hooks";
11730
11920
  import "commander";
11731
11921
  import {
11732
11922
  estimateTokens as estimateTokens3,
11733
- findProjectRoot as findProjectRoot38,
11734
- resolveHaivePaths as resolveHaivePaths35
11923
+ findProjectRoot as findProjectRoot39,
11924
+ resolveHaivePaths as resolveHaivePaths36
11735
11925
  } from "@hiveai/core";
11736
11926
  function registerBench(program2) {
11737
11927
  program2.command("bench").description("Self-test the local hAIve setup: runs core MCP tools against this project and reports latency + payload size.").option("-t, --task <task>", "task description for ranking-aware tools", "audit dependencies for security risks").option("--json", "emit JSON instead of a table", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11738
- const root = findProjectRoot38(opts.dir);
11739
- const paths = resolveHaivePaths35(root);
11928
+ const root = findProjectRoot39(opts.dir);
11929
+ const paths = resolveHaivePaths36(root);
11740
11930
  const ctx = { paths };
11741
11931
  const task = opts.task ?? "audit dependencies for security risks";
11742
11932
  const scenarios = [
@@ -11855,11 +12045,11 @@ function summarize(name, t0, payload, notes) {
11855
12045
  }
11856
12046
 
11857
12047
  // src/commands/benchmark.ts
11858
- import { existsSync as existsSync59 } from "fs";
12048
+ import { existsSync as existsSync61 } from "fs";
11859
12049
  import { readdir as readdir5, readFile as readFile19, writeFile as writeFile28 } from "fs/promises";
11860
12050
  import path41 from "path";
11861
12051
  import "commander";
11862
- import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot39 } from "@hiveai/core";
12052
+ import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot40 } from "@hiveai/core";
11863
12053
  function registerBenchmark(program2) {
11864
12054
  const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
11865
12055
  benchmark.command("report").description("Summarize BENCHMARK_AGENT_REPORT.md files from a paired hAIve/plain agent benchmark.").option("-d, --dir <dir>", "benchmark root", "benchmarks/agent-benchmark").option("--out <file>", "write a Markdown report").option("--json", "emit JSON", false).action(async (opts) => {
@@ -11899,18 +12089,18 @@ function registerBenchmark(program2) {
11899
12089
  function resolveBenchmarkRoot(dir) {
11900
12090
  const candidate = dir ?? "benchmarks/agent-benchmark";
11901
12091
  if (path41.isAbsolute(candidate)) return candidate;
11902
- const projectRoot = findProjectRoot39(process.cwd());
12092
+ const projectRoot = findProjectRoot40(process.cwd());
11903
12093
  return path41.join(projectRoot, candidate);
11904
12094
  }
11905
12095
  async function collectRows(root) {
11906
- if (!existsSync59(root)) throw new Error(`Benchmark directory not found: ${root}`);
12096
+ if (!existsSync61(root)) throw new Error(`Benchmark directory not found: ${root}`);
11907
12097
  const entries = await readdir5(root, { withFileTypes: true });
11908
12098
  const rows = [];
11909
12099
  for (const entry of entries) {
11910
12100
  if (!entry.isDirectory()) continue;
11911
12101
  const fixtureDir = path41.join(root, entry.name);
11912
12102
  const reportFile = path41.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
11913
- if (!existsSync59(reportFile)) continue;
12103
+ if (!existsSync61(reportFile)) continue;
11914
12104
  const report = await readFile19(reportFile, "utf8");
11915
12105
  rows.push(parseAgentReport(entry.name, report));
11916
12106
  }
@@ -11999,21 +12189,178 @@ function escapeRegExp(value) {
11999
12189
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12000
12190
  }
12001
12191
 
12002
- // src/commands/memory-suggest.ts
12003
- import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
12004
- import { existsSync as existsSync60 } from "fs";
12192
+ // src/commands/eval.ts
12193
+ import { readFile as readFile20, writeFile as writeFile29 } from "fs/promises";
12194
+ import { existsSync as existsSync63 } from "fs";
12005
12195
  import path43 from "path";
12006
12196
  import "commander";
12197
+ import {
12198
+ aggregateRetrieval,
12199
+ aggregateSensors,
12200
+ buildReport,
12201
+ findProjectRoot as findProjectRoot41,
12202
+ resolveHaivePaths as resolveHaivePaths37,
12203
+ scoreRetrievalCase,
12204
+ scoreSensorCase,
12205
+ synthesizeSelfEvalCases
12206
+ } from "@hiveai/core";
12207
+ function registerEval(program2) {
12208
+ program2.command("eval").description(
12209
+ "Rigorous, repeatable quality eval: do the right memories surface (retrieval) and do the right sensors fire (catch-rate)? Emits a chiffr\xE9 0\u2013100 score. Uses .ai/eval cases via --spec, or auto-synthesizes cases from anchored memories."
12210
+ ).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("-d, --dir <dir>", "project root").action(async (opts) => {
12211
+ const root = findProjectRoot41(opts.dir);
12212
+ const paths = resolveHaivePaths37(root);
12213
+ if (!existsSync63(paths.memoriesDir)) {
12214
+ ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
12215
+ process.exitCode = 1;
12216
+ return;
12217
+ }
12218
+ const k = Math.max(1, parseInt(opts.top ?? "8", 10) || 8);
12219
+ const ctx = { paths };
12220
+ const spec = await resolveSpec(opts, paths.memoriesDir);
12221
+ if ((spec.retrieval?.length ?? 0) === 0 && (spec.sensors?.length ?? 0) === 0) {
12222
+ ui.warn("No eval cases (no anchored memories and no --spec). Nothing to score.");
12223
+ return;
12224
+ }
12225
+ let retrievalAgg = null;
12226
+ if (spec.retrieval && spec.retrieval.length > 0) {
12227
+ const results = [];
12228
+ for (const c of spec.retrieval) {
12229
+ const surfaced = await runRetrieval(c, k, ctx);
12230
+ results.push(scoreRetrievalCase(c.name, c.expect_ids, surfaced));
12231
+ }
12232
+ retrievalAgg = aggregateRetrieval(results);
12233
+ }
12234
+ let sensorAgg = null;
12235
+ if (spec.sensors && spec.sensors.length > 0) {
12236
+ const results = [];
12237
+ for (const c of spec.sensors) {
12238
+ const fired = await runSensorCase(c, ctx);
12239
+ results.push(scoreSensorCase(c.name, c.expect_fire_ids, fired));
12240
+ }
12241
+ sensorAgg = aggregateSensors(results);
12242
+ }
12243
+ const report = buildReport(retrievalAgg, sensorAgg);
12244
+ if (opts.json) {
12245
+ console.log(JSON.stringify({ root, k, report }, null, 2));
12246
+ return;
12247
+ }
12248
+ const md = renderMarkdown2(root, k, report);
12249
+ if (opts.out) {
12250
+ const outFile = path43.isAbsolute(opts.out) ? opts.out : path43.join(root, opts.out);
12251
+ await writeFile29(outFile, md, "utf8");
12252
+ ui.success(`wrote ${path43.relative(process.cwd(), outFile)}`);
12253
+ return;
12254
+ }
12255
+ console.log(md);
12256
+ });
12257
+ }
12258
+ async function resolveSpec(opts, memoriesDir) {
12259
+ if (opts.spec) {
12260
+ const file = path43.resolve(opts.spec);
12261
+ const raw = await readFile20(file, "utf8");
12262
+ return JSON.parse(raw);
12263
+ }
12264
+ const memories = await loadMemoriesFromDir26(memoriesDir);
12265
+ return { retrieval: synthesizeSelfEvalCases(memories, { includeFiles: !opts.semanticOnly }) };
12266
+ }
12267
+ async function runRetrieval(c, k, ctx) {
12268
+ const out = await getBriefing(
12269
+ {
12270
+ task: c.task,
12271
+ files: c.files ?? [],
12272
+ symbols: c.symbols ?? [],
12273
+ max_tokens: 6e3,
12274
+ max_memories: k,
12275
+ include_project_context: false,
12276
+ include_module_contexts: false,
12277
+ semantic: true,
12278
+ include_stale: false,
12279
+ track: false,
12280
+ format: "compact",
12281
+ min_semantic_score: 0
12282
+ },
12283
+ ctx
12284
+ );
12285
+ return out.memories.map((m) => m.id);
12286
+ }
12287
+ async function runSensorCase(c, ctx) {
12288
+ const out = await antiPatternsCheck(
12289
+ { diff: c.diff, paths: c.paths ?? [], limit: 50, semantic: false },
12290
+ ctx
12291
+ );
12292
+ return out.warnings.filter((w) => w.reasons.includes("sensor")).map((w) => w.id);
12293
+ }
12294
+ function pct(n) {
12295
+ return `${Math.round(n * 100)}%`;
12296
+ }
12297
+ function renderMarkdown2(root, k, report) {
12298
+ const lines = [
12299
+ "# hAIve eval report",
12300
+ "",
12301
+ `Project: \`${root}\` \xB7 top-k: ${k}`,
12302
+ "",
12303
+ `## Overall score: ${report.score}/100`,
12304
+ ""
12305
+ ];
12306
+ if (report.retrieval) {
12307
+ const r = report.retrieval;
12308
+ lines.push(
12309
+ "## Retrieval",
12310
+ "",
12311
+ `- cases: ${r.cases.length}`,
12312
+ `- mean recall: ${pct(r.mean_recall)}`,
12313
+ `- mean precision: ${pct(r.mean_precision)}`,
12314
+ `- MRR: ${r.mrr.toFixed(3)}`,
12315
+ ""
12316
+ );
12317
+ const misses = r.cases.filter((c) => c.misses.length > 0);
12318
+ if (misses.length > 0) {
12319
+ lines.push(`### ${misses.length} retrieval miss(es)`, "");
12320
+ for (const c of misses.slice(0, 25)) {
12321
+ lines.push(`- \`${c.name}\` \u2014 expected not in top-${k}`);
12322
+ }
12323
+ lines.push("");
12324
+ }
12325
+ }
12326
+ if (report.sensors) {
12327
+ const s = report.sensors;
12328
+ lines.push("## Sensors", "", `- cases: ${s.cases.length}`, `- catch-rate: ${pct(s.catch_rate)}`, "");
12329
+ const misses = s.cases.filter((c) => c.misses.length > 0);
12330
+ if (misses.length > 0) {
12331
+ lines.push(`### ${misses.length} sensor miss(es)`, "");
12332
+ for (const c of misses.slice(0, 25)) {
12333
+ lines.push(`- \`${c.name}\` \u2014 sensor did not fire (expected: ${c.misses.join(", ")})`);
12334
+ }
12335
+ lines.push("");
12336
+ }
12337
+ }
12338
+ lines.push(
12339
+ "## Reading",
12340
+ "",
12341
+ "Retrieval recall = share of expected memories that surfaced in the briefing top-k.",
12342
+ "MRR rewards ranking the right memory high. Catch-rate = share of known-bad diffs a sensor flagged.",
12343
+ "Run in CI to fail the build on a ranking/sensor regression.",
12344
+ ""
12345
+ );
12346
+ return lines.join("\n");
12347
+ }
12348
+
12349
+ // src/commands/memory-suggest.ts
12350
+ import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
12351
+ import { existsSync as existsSync64 } from "fs";
12352
+ import path44 from "path";
12353
+ import "commander";
12007
12354
  import {
12008
12355
  aggregateUsage as aggregateUsage2,
12009
12356
  buildFrontmatter as buildFrontmatter11,
12010
- findProjectRoot as findProjectRoot40,
12357
+ findProjectRoot as findProjectRoot42,
12011
12358
  loadConfig as loadConfig9,
12012
- loadMemoriesFromDir as loadMemoriesFromDir30,
12359
+ loadMemoriesFromDir as loadMemoriesFromDir31,
12013
12360
  memoryFilePath as memoryFilePath10,
12014
12361
  parseSince as parseSince2,
12015
12362
  readUsageEvents as readUsageEvents3,
12016
- resolveHaivePaths as resolveHaivePaths36,
12363
+ resolveHaivePaths as resolveHaivePaths38,
12017
12364
  serializeMemory as serializeMemory24
12018
12365
  } from "@hiveai/core";
12019
12366
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
@@ -12030,8 +12377,8 @@ function registerMemorySuggest(memory2) {
12030
12377
  memory2.command("suggest").description(
12031
12378
  "Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to save the top-N suggestions using the project defaults.\n In autopilot, suggestions land as validated team records; in manual mode they stay draft."
12032
12379
  ).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of saved memories (personal | team; default: config default)").option("--auto-save", "save top-N suggestions as memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
12033
- const root = findProjectRoot40(opts.dir);
12034
- const paths = resolveHaivePaths36(root);
12380
+ const root = findProjectRoot42(opts.dir);
12381
+ const paths = resolveHaivePaths38(root);
12035
12382
  const events = await readUsageEvents3(paths);
12036
12383
  if (events.length === 0) {
12037
12384
  if (opts.json) {
@@ -12080,7 +12427,7 @@ function registerMemorySuggest(memory2) {
12080
12427
  }
12081
12428
  const created = [];
12082
12429
  const skipped = [];
12083
- const existing = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
12430
+ const existing = existsSync64(paths.memoriesDir) ? await loadMemoriesFromDir31(paths.memoriesDir) : [];
12084
12431
  for (const s of top) {
12085
12432
  const slug = slugify2(s.query);
12086
12433
  if (!slug) {
@@ -12103,13 +12450,13 @@ function registerMemorySuggest(memory2) {
12103
12450
  });
12104
12451
  const body = renderTemplate(s, fm.id, status);
12105
12452
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
12106
- await mkdir18(path43.dirname(file), { recursive: true });
12107
- if (existsSync60(file)) {
12108
- skipped.push({ query: s.query, reason: `file already exists at ${path43.relative(root, file)}` });
12453
+ await mkdir18(path44.dirname(file), { recursive: true });
12454
+ if (existsSync64(file)) {
12455
+ skipped.push({ query: s.query, reason: `file already exists at ${path44.relative(root, file)}` });
12109
12456
  continue;
12110
12457
  }
12111
- await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
12112
- created.push({ id: fm.id, file: path43.relative(root, file), query: s.query });
12458
+ await writeFile30(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
12459
+ created.push({ id: fm.id, file: path44.relative(root, file), query: s.query });
12113
12460
  }
12114
12461
  if (opts.json) {
12115
12462
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -12207,18 +12554,18 @@ function truncate2(text, max) {
12207
12554
  }
12208
12555
 
12209
12556
  // src/commands/memory-archive.ts
12210
- import { existsSync as existsSync61 } from "fs";
12211
- import { writeFile as writeFile30 } from "fs/promises";
12212
- import path44 from "path";
12557
+ import { existsSync as existsSync65 } from "fs";
12558
+ import { writeFile as writeFile31 } from "fs/promises";
12559
+ import path45 from "path";
12213
12560
  import "commander";
12214
12561
  import {
12215
- findProjectRoot as findProjectRoot41,
12216
- getUsage as getUsage18,
12562
+ findProjectRoot as findProjectRoot43,
12563
+ getUsage as getUsage20,
12217
12564
  retirementSignal as retirementSignal2,
12218
12565
  loadConfig as loadConfig10,
12219
- loadMemoriesFromDir as loadMemoriesFromDir31,
12220
- loadUsageIndex as loadUsageIndex24,
12221
- resolveHaivePaths as resolveHaivePaths37,
12566
+ loadMemoriesFromDir as loadMemoriesFromDir33,
12567
+ loadUsageIndex as loadUsageIndex26,
12568
+ resolveHaivePaths as resolveHaivePaths39,
12222
12569
  serializeMemory as serializeMemory25
12223
12570
  } from "@hiveai/core";
12224
12571
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
@@ -12226,9 +12573,9 @@ function registerMemoryArchive(memory2) {
12226
12573
  memory2.command("archive").description(
12227
12574
  "Archive obsolete memories: marks status='deprecated' for memories not read in N days\n whose anchored paths have all disappeared (or have no anchor at all).\n\n Defaults to a DRY RUN \u2014 pass --apply to actually rewrite files.\n Targets `attempt` memories by default since they age the fastest.\n\n Recover later with `haive memory edit <id>` to set status back to validated."
12228
12575
  ).option("--since <window>", "minimum age since last read (e.g. '180d', '6m'). Default: enforcement.decayAfterDays or 180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--unread", "decay by unread-age ALONE (ignore anchor status) \u2014 more aggressive corpus hygiene", false).option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
12229
- const root = findProjectRoot41(opts.dir);
12230
- const paths = resolveHaivePaths37(root);
12231
- if (!existsSync61(paths.memoriesDir)) {
12576
+ const root = findProjectRoot43(opts.dir);
12577
+ const paths = resolveHaivePaths39(root);
12578
+ if (!existsSync65(paths.memoriesDir)) {
12232
12579
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
12233
12580
  process.exitCode = 1;
12234
12581
  return;
@@ -12242,8 +12589,8 @@ function registerMemoryArchive(memory2) {
12242
12589
  return;
12243
12590
  }
12244
12591
  const cutoff = Date.now() - minDays * MS_PER_DAY2;
12245
- const all = await loadMemoriesFromDir31(paths.memoriesDir);
12246
- const usage = await loadUsageIndex24(paths);
12592
+ const all = await loadMemoriesFromDir33(paths.memoriesDir);
12593
+ const usage = await loadUsageIndex26(paths);
12247
12594
  const typeFilter = opts.type === "all" ? null : opts.type ?? "attempt";
12248
12595
  const candidates = [];
12249
12596
  for (const { memory: mem, filePath } of all) {
@@ -12253,10 +12600,10 @@ function registerMemoryArchive(memory2) {
12253
12600
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
12254
12601
  const retired = retirementSignal2(fm, mem.body);
12255
12602
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
12256
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync61(path44.join(paths.root, p)));
12603
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync65(path45.join(paths.root, p)));
12257
12604
  const isAnchorless = !hasAnyAnchor;
12258
12605
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
12259
- const u = getUsage18(usage, fm.id);
12606
+ const u = getUsage20(usage, fm.id);
12260
12607
  const lastSeen = u.last_read_at ?? fm.created_at;
12261
12608
  if (!retired.retired && Date.parse(lastSeen) >= cutoff) continue;
12262
12609
  const reason = retired.retired ? `retired lifecycle signal: ${retired.reason ?? "unknown"}` : isAnchorless ? `anchorless and not read since ${lastSeen.slice(0, 10)}` : allPathsGone ? `all ${fm.anchor.paths.length} anchored path(s) missing and not read since ${lastSeen.slice(0, 10)}` : `not read since ${lastSeen.slice(0, 10)} (unread decay)`;
@@ -12302,7 +12649,7 @@ function registerMemoryArchive(memory2) {
12302
12649
  if (!found) continue;
12303
12650
  const fm = { ...found.memory.frontmatter, status: "deprecated" };
12304
12651
  try {
12305
- await writeFile30(c.filePath, serializeMemory25({ frontmatter: fm, body: found.memory.body }), "utf8");
12652
+ await writeFile31(c.filePath, serializeMemory25({ frontmatter: fm, body: found.memory.body }), "utf8");
12306
12653
  archived++;
12307
12654
  } catch (err) {
12308
12655
  if (!opts.json) {
@@ -12328,34 +12675,34 @@ function parseDays(input) {
12328
12675
  }
12329
12676
 
12330
12677
  // src/commands/doctor.ts
12331
- import { existsSync as existsSync63, statSync as statSync2 } from "fs";
12332
- import { readFile as readFile20, stat, writeFile as writeFile31 } from "fs/promises";
12333
- import path45 from "path";
12678
+ import { existsSync as existsSync66, statSync as statSync2 } from "fs";
12679
+ import { readFile as readFile21, stat, writeFile as writeFile33 } from "fs/promises";
12680
+ import path46 from "path";
12334
12681
  import { execFileSync, execSync as execSync3 } from "child_process";
12335
12682
  import "commander";
12336
12683
  import {
12337
12684
  codeMapPath as codeMapPath2,
12338
- findProjectRoot as findProjectRoot42,
12339
- getUsage as getUsage19,
12685
+ findProjectRoot as findProjectRoot44,
12686
+ getUsage as getUsage21,
12340
12687
  isStackPackSeed as isStackPackSeed4,
12341
12688
  loadCodeMap as loadCodeMap7,
12342
12689
  loadConfig as loadConfig11,
12343
- loadMemoriesFromDir as loadMemoriesFromDir33,
12344
- loadUsageIndex as loadUsageIndex25,
12690
+ loadMemoriesFromDir as loadMemoriesFromDir34,
12691
+ loadUsageIndex as loadUsageIndex27,
12345
12692
  readUsageEvents as readUsageEvents4,
12346
- resolveHaivePaths as resolveHaivePaths38
12693
+ resolveHaivePaths as resolveHaivePaths40
12347
12694
  } from "@hiveai/core";
12348
12695
  var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
12349
12696
  function registerDoctor(program2) {
12350
12697
  program2.command("doctor").description(
12351
12698
  "Analyze the local hAIve setup and emit actionable recommendations.\n\n Inspects: project-context status, memory health (stale/anchorless/decay/pending),\n code-map freshness, usage log signals (low-hit briefings, repeated empty searches).\n\n Read-only by default. Pass --fix to apply safe autopilot repairs."
12352
12699
  ).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("--dry-run", "with --fix, show delegated repairs without applying them", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
12353
- const root = findProjectRoot42(opts.dir);
12354
- const paths = resolveHaivePaths38(root);
12700
+ const root = findProjectRoot44(opts.dir);
12701
+ const paths = resolveHaivePaths40(root);
12355
12702
  const findings = [];
12356
12703
  const repairs = [];
12357
12704
  const config = await loadConfig11(paths);
12358
- if (!existsSync63(paths.haiveDir)) {
12705
+ if (!existsSync66(paths.haiveDir)) {
12359
12706
  findings.push({
12360
12707
  severity: "error",
12361
12708
  code: "not-initialized",
@@ -12376,7 +12723,7 @@ function registerDoctor(program2) {
12376
12723
  })
12377
12724
  );
12378
12725
  }
12379
- if (!existsSync63(paths.projectContext)) {
12726
+ if (!existsSync66(paths.projectContext)) {
12380
12727
  findings.push({
12381
12728
  severity: "warn",
12382
12729
  code: "no-project-context",
@@ -12384,8 +12731,8 @@ function registerDoctor(program2) {
12384
12731
  fix: "haive init"
12385
12732
  });
12386
12733
  } else {
12387
- const { readFile: readFile24 } = await import("fs/promises");
12388
- const content = await readFile24(paths.projectContext, "utf8");
12734
+ const { readFile: readFile25 } = await import("fs/promises");
12735
+ const content = await readFile25(paths.projectContext, "utf8");
12389
12736
  const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `haive init`");
12390
12737
  if (isTemplate) {
12391
12738
  findings.push({
@@ -12405,7 +12752,7 @@ function registerDoctor(program2) {
12405
12752
  });
12406
12753
  }
12407
12754
  }
12408
- const memories = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
12755
+ const memories = existsSync66(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
12409
12756
  const now = Date.now();
12410
12757
  if (memories.length === 0) {
12411
12758
  findings.push({
@@ -12414,7 +12761,7 @@ function registerDoctor(program2) {
12414
12761
  message: "No memories yet. Capture knowledge as agents work via mem_save / mem_observe / mem_tried."
12415
12762
  });
12416
12763
  } else {
12417
- const usage = await loadUsageIndex25(paths);
12764
+ const usage = await loadUsageIndex27(paths);
12418
12765
  const stale = memories.filter((m) => m.memory.frontmatter.status === "stale");
12419
12766
  if (stale.length > 0) {
12420
12767
  findings.push({
@@ -12471,7 +12818,7 @@ function registerDoctor(program2) {
12471
12818
  }
12472
12819
  const decayCandidates = memories.filter((m) => {
12473
12820
  if (m.memory.frontmatter.status !== "validated") return false;
12474
- const u = getUsage19(usage, m.memory.frontmatter.id);
12821
+ const u = getUsage21(usage, m.memory.frontmatter.id);
12475
12822
  const last = u.last_read_at ?? m.memory.frontmatter.created_at;
12476
12823
  return (now - Date.parse(last)) / MS_PER_DAY3 > 180;
12477
12824
  });
@@ -12555,12 +12902,12 @@ function registerDoctor(program2) {
12555
12902
  }
12556
12903
  }
12557
12904
  if (config.enforcement?.requireBriefingFirst) {
12558
- const claudeSettings = path45.join(root, ".claude", "settings.local.json");
12905
+ const claudeSettings = path46.join(root, ".claude", "settings.local.json");
12559
12906
  let hasClaudeEnforcement = false;
12560
- if (existsSync63(claudeSettings)) {
12907
+ if (existsSync66(claudeSettings)) {
12561
12908
  try {
12562
- const { readFile: readFile24 } = await import("fs/promises");
12563
- const raw = await readFile24(claudeSettings, "utf8");
12909
+ const { readFile: readFile25 } = await import("fs/promises");
12910
+ const raw = await readFile25(claudeSettings, "utf8");
12564
12911
  hasClaudeEnforcement = raw.includes("haive enforce session-start") && raw.includes("haive enforce pre-tool-use");
12565
12912
  } catch {
12566
12913
  hasClaudeEnforcement = false;
@@ -12583,14 +12930,14 @@ function registerDoctor(program2) {
12583
12930
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12584
12931
  });
12585
12932
  }
12586
- findings.push(...await collectInstallFindings(root, "0.10.9"));
12933
+ findings.push(...await collectInstallFindings(root, "0.11.0"));
12587
12934
  try {
12588
12935
  const legacyRaw = execSync3("haive-mcp --version", {
12589
12936
  encoding: "utf8",
12590
12937
  timeout: 3e3,
12591
12938
  stdio: ["ignore", "pipe", "ignore"]
12592
12939
  }).trim();
12593
- const cliVersion = "0.10.9";
12940
+ const cliVersion = "0.11.0";
12594
12941
  if (legacyRaw && legacyRaw !== cliVersion) {
12595
12942
  findings.push({
12596
12943
  severity: "warn",
@@ -12606,20 +12953,20 @@ npm uninstall -g @hiveai/mcp`
12606
12953
  }
12607
12954
  {
12608
12955
  const configPaths = [
12609
- path45.join(root, ".mcp.json"),
12610
- path45.join(root, ".cursor", "mcp.json"),
12611
- path45.join(root, ".vscode", "mcp.json")
12956
+ path46.join(root, ".mcp.json"),
12957
+ path46.join(root, ".cursor", "mcp.json"),
12958
+ path46.join(root, ".vscode", "mcp.json")
12612
12959
  ];
12613
12960
  const staleConfigs = [];
12614
12961
  for (const cfgPath of configPaths) {
12615
- if (!existsSync63(cfgPath)) continue;
12962
+ if (!existsSync66(cfgPath)) continue;
12616
12963
  try {
12617
- const raw = await readFile20(cfgPath, "utf8");
12964
+ const raw = await readFile21(cfgPath, "utf8");
12618
12965
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
12619
- staleConfigs.push(path45.relative(root, cfgPath));
12966
+ staleConfigs.push(path46.relative(root, cfgPath));
12620
12967
  if (opts.fix && !opts.dryRun) {
12621
12968
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
12622
- await writeFile31(cfgPath, updated, "utf8");
12969
+ await writeFile33(cfgPath, updated, "utf8");
12623
12970
  }
12624
12971
  }
12625
12972
  } catch {
@@ -12790,23 +13137,23 @@ async function collectHarnessCoverageFindings(codeMap, memories) {
12790
13137
  }
12791
13138
  }
12792
13139
  const covered = coveredFiles.size;
12793
- const pct = Math.round(covered / total * 100);
13140
+ const pct2 = Math.round(covered / total * 100);
12794
13141
  const uncovered = codeFiles.filter((f) => !coveredFiles.has(f)).sort((a, b) => {
12795
13142
  const depthA = a.split("/").length;
12796
13143
  const depthB = b.split("/").length;
12797
13144
  if (depthA !== depthB) return depthA - depthB;
12798
13145
  return a.localeCompare(b);
12799
13146
  }).slice(0, 5);
12800
- const coverageDesc = pct < 10 && total > 10 ? "Low coverage \u2014 add memory anchors on key modules to improve harness enforcement." : pct < 50 ? "Partial coverage \u2014 useful but not yet broad enough to call the harness mature." : pct < 80 ? "Good coverage \u2014 critical modules are increasingly protected." : "Good harness coverage.";
13147
+ const coverageDesc = pct2 < 10 && total > 10 ? "Low coverage \u2014 add memory anchors on key modules to improve harness enforcement." : pct2 < 50 ? "Partial coverage \u2014 useful but not yet broad enough to call the harness mature." : pct2 < 80 ? "Good coverage \u2014 critical modules are increasingly protected." : "Good harness coverage.";
12801
13148
  const uncoveredHint = uncovered.length > 0 ? `
12802
13149
  Top uncovered: ${uncovered.map((f) => `\`${f}\``).join(", ")}` : "";
12803
13150
  const findings = [];
12804
13151
  findings.push({
12805
13152
  severity: "info",
12806
13153
  code: "harness-coverage",
12807
- coverage_percent: pct,
12808
- message: `${covered}/${total} code-map files have validated memory anchors (${pct}%). ` + coverageDesc + uncoveredHint,
12809
- fix: pct < 50 && total > 10 ? `haive memory add --type gotcha|convention|architecture --paths <key-file> --scope team` : void 0,
13154
+ coverage_percent: pct2,
13155
+ message: `${covered}/${total} code-map files have validated memory anchors (${pct2}%). ` + coverageDesc + uncoveredHint,
13156
+ fix: pct2 < 50 && total > 10 ? `haive memory add --type gotcha|convention|architecture --paths <key-file> --scope team` : void 0,
12810
13157
  section: "Harness coverage"
12811
13158
  });
12812
13159
  return findings;
@@ -12906,9 +13253,9 @@ which -a haive`
12906
13253
  ".vscode/mcp.json"
12907
13254
  ];
12908
13255
  for (const rel of integrationFiles) {
12909
- const file = path45.join(root, rel);
12910
- if (!existsSync63(file)) continue;
12911
- const text = await readFile20(file, "utf8").catch(() => "");
13256
+ const file = path46.join(root, rel);
13257
+ if (!existsSync66(file)) continue;
13258
+ const text = await readFile21(file, "utf8").catch(() => "");
12912
13259
  for (const bin of extractAbsoluteHaiveBins(text)) {
12913
13260
  const version = versionForBinary(bin);
12914
13261
  if (!version) {
@@ -12932,7 +13279,7 @@ which -a haive`
12932
13279
  }
12933
13280
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
12934
13281
  const findings = [];
12935
- const rootPkg = await readJson(path45.join(root, "package.json"));
13282
+ const rootPkg = await readJson(path46.join(root, "package.json"));
12936
13283
  const workspacePackages = [
12937
13284
  "packages/core/package.json",
12938
13285
  "packages/embeddings/package.json",
@@ -12941,7 +13288,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
12941
13288
  ];
12942
13289
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
12943
13290
  rel,
12944
- pkg: await readJson(path45.join(root, rel))
13291
+ pkg: await readJson(path46.join(root, rel))
12945
13292
  })))).filter((item) => item.pkg);
12946
13293
  const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
12947
13294
  if (!isHaiveWorkspace) return findings;
@@ -13003,9 +13350,9 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
13003
13350
  }
13004
13351
  }
13005
13352
  async function readJson(file) {
13006
- if (!existsSync63(file)) return null;
13353
+ if (!existsSync66(file)) return null;
13007
13354
  try {
13008
- return JSON.parse(await readFile20(file, "utf8"));
13355
+ return JSON.parse(await readFile21(file, "utf8"));
13009
13356
  } catch {
13010
13357
  return null;
13011
13358
  }
@@ -13050,22 +13397,22 @@ function extractAbsoluteHaiveBins(text) {
13050
13397
  }
13051
13398
 
13052
13399
  // src/commands/playback.ts
13053
- import { existsSync as existsSync64 } from "fs";
13400
+ import { existsSync as existsSync67 } from "fs";
13054
13401
  import "commander";
13055
13402
  import {
13056
- findProjectRoot as findProjectRoot43,
13057
- loadMemoriesFromDir as loadMemoriesFromDir34,
13403
+ findProjectRoot as findProjectRoot45,
13404
+ loadMemoriesFromDir as loadMemoriesFromDir35,
13058
13405
  parseSince as parseSince3,
13059
13406
  readUsageEvents as readUsageEvents5,
13060
- resolveHaivePaths as resolveHaivePaths39
13407
+ resolveHaivePaths as resolveHaivePaths41
13061
13408
  } from "@hiveai/core";
13062
13409
  var MS_PER_MINUTE = 6e4;
13063
13410
  function registerPlayback(program2) {
13064
13411
  program2.command("playback").description(
13065
13412
  "Replay past sessions from the usage log. For each session, show:\n - tool calls (what kind, how many)\n - briefing tasks asked\n - memories that have been created since then (that the session didn't have)\n\n Useful to ask 'would today's haive have helped past me on this task?'"
13066
13413
  ).option("--since <window>", "limit to events in this window (e.g. '7d')", "30d").option("--session-gap <minutes>", "minutes of inactivity that splits a session", "30").option("--limit <n>", "show at most this many sessions (newest first)", "10").option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
13067
- const root = findProjectRoot43(opts.dir);
13068
- const paths = resolveHaivePaths39(root);
13414
+ const root = findProjectRoot45(opts.dir);
13415
+ const paths = resolveHaivePaths41(root);
13069
13416
  const events = await readUsageEvents5(paths);
13070
13417
  if (events.length === 0) {
13071
13418
  if (opts.json) {
@@ -13080,7 +13427,7 @@ function registerPlayback(program2) {
13080
13427
  const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
13081
13428
  const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
13082
13429
  const sessions = bucketSessions(filtered, gapMs);
13083
- const all = existsSync64(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
13430
+ const all = existsSync67(paths.memoriesDir) ? await loadMemoriesFromDir35(paths.memoriesDir) : [];
13084
13431
  const memByCreatedAt = all.filter(({ memory: memory2 }) => memory2.frontmatter.type !== "session_recap").map(({ memory: memory2 }) => ({ id: memory2.frontmatter.id, at: Date.parse(memory2.frontmatter.created_at) })).sort((a, b) => a.at - b.at);
13085
13432
  const enriched = sessions.map((s, i) => {
13086
13433
  const startMs = Date.parse(s.start);
@@ -13171,9 +13518,9 @@ import { spawn as spawn5 } from "child_process";
13171
13518
  import "commander";
13172
13519
  import {
13173
13520
  antiPatternGateParams,
13174
- findProjectRoot as findProjectRoot44,
13521
+ findProjectRoot as findProjectRoot46,
13175
13522
  loadConfig as loadConfig12,
13176
- resolveHaivePaths as resolveHaivePaths40
13523
+ resolveHaivePaths as resolveHaivePaths42
13177
13524
  } from "@hiveai/core";
13178
13525
  function registerPrecommit(program2) {
13179
13526
  program2.command("precommit").description(
@@ -13185,8 +13532,8 @@ function registerPrecommit(program2) {
13185
13532
  "--no-anchored-blocks",
13186
13533
  "do not block on anchored, diff-corroborated anti-patterns (only block on very strong semantic matches)"
13187
13534
  ).option("--json", "emit JSON instead of human-readable output", false).option("--paths <paths...>", "explicit paths to check (skips git diff)").option("-d, --dir <dir>", "project root").action(async (opts) => {
13188
- const root = findProjectRoot44(opts.dir);
13189
- const paths = resolveHaivePaths40(root);
13535
+ const root = findProjectRoot46(opts.dir);
13536
+ const paths = resolveHaivePaths42(root);
13190
13537
  const ctx = { paths };
13191
13538
  const config = await loadConfig12(paths);
13192
13539
  const gate = config.enforcement?.antiPatternGate ?? "anchored";
@@ -13322,12 +13669,12 @@ function runCommand3(cmd, args, cwd) {
13322
13669
  }
13323
13670
 
13324
13671
  // src/commands/welcome.ts
13325
- import { existsSync as existsSync65 } from "fs";
13672
+ import { existsSync as existsSync68 } from "fs";
13326
13673
  import "commander";
13327
13674
  import {
13328
- findProjectRoot as findProjectRoot45,
13329
- loadMemoriesFromDir as loadMemoriesFromDir35,
13330
- resolveHaivePaths as resolveHaivePaths41
13675
+ findProjectRoot as findProjectRoot47,
13676
+ loadMemoriesFromDir as loadMemoriesFromDir36,
13677
+ resolveHaivePaths as resolveHaivePaths43
13331
13678
  } from "@hiveai/core";
13332
13679
  var TYPE_RANK = {
13333
13680
  skill: 0,
@@ -13342,14 +13689,14 @@ function registerWelcome(program2) {
13342
13689
  program2.command("welcome").description(
13343
13690
  "Onboarding checklist: ranks validated/proposed **team** memories by type.\nUse after `haive init` so new devs skim institutional knowledge quickly.\n\n haive welcome\n haive welcome --limit 15\n"
13344
13691
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
13345
- const root = findProjectRoot45(opts.dir);
13346
- const paths = resolveHaivePaths41(root);
13347
- if (!existsSync65(paths.memoriesDir)) {
13692
+ const root = findProjectRoot47(opts.dir);
13693
+ const paths = resolveHaivePaths43(root);
13694
+ if (!existsSync68(paths.memoriesDir)) {
13348
13695
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
13349
13696
  process.exitCode = 1;
13350
13697
  return;
13351
13698
  }
13352
- const all = await loadMemoriesFromDir35(paths.memoriesDir);
13699
+ const all = await loadMemoriesFromDir36(paths.memoriesDir);
13353
13700
  const team = all.filter(
13354
13701
  ({ memory: memory2 }) => memory2.frontmatter.scope === "team" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && memory2.frontmatter.status !== "stale" && memory2.frontmatter.type !== "session_recap"
13355
13702
  );
@@ -13403,27 +13750,27 @@ function registerMemorySuggestTopic(memory2) {
13403
13750
  }
13404
13751
 
13405
13752
  // src/commands/resolve-project.ts
13406
- import path46 from "path";
13753
+ import path47 from "path";
13407
13754
  import "commander";
13408
13755
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
13409
13756
  function registerResolveProject(program2) {
13410
13757
  program2.command("resolve-project").description(
13411
13758
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
13412
13759
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
13413
- const info = resolveProjectInfo2({ cwd: path46.resolve(opts.dir) });
13760
+ const info = resolveProjectInfo2({ cwd: path47.resolve(opts.dir) });
13414
13761
  console.log(JSON.stringify({ ok: true, info }, null, 2));
13415
13762
  });
13416
13763
  }
13417
13764
 
13418
13765
  // src/commands/runtime-journal.ts
13419
- import { existsSync as existsSync66 } from "fs";
13420
- import path47 from "path";
13766
+ import { existsSync as existsSync69 } from "fs";
13767
+ import path48 from "path";
13421
13768
  import "commander";
13422
13769
  import {
13423
13770
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
13424
- findProjectRoot as findProjectRoot46,
13771
+ findProjectRoot as findProjectRoot48,
13425
13772
  readRuntimeJournalTail as readRuntimeJournalTail2,
13426
- resolveHaivePaths as resolveHaivePaths42
13773
+ resolveHaivePaths as resolveHaivePaths44
13427
13774
  } from "@hiveai/core";
13428
13775
  function registerRuntime(program2) {
13429
13776
  const runtime = program2.command("runtime").description(
@@ -13431,18 +13778,18 @@ function registerRuntime(program2) {
13431
13778
  );
13432
13779
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
13433
13780
  journal.command("append").description("Append one JSON line to .ai/.runtime/session-journal.ndjson").argument("<message>", "short text to log").option("-k, --kind <kind>", "note | session_end | mcp", "note").option("-d, --dir <dir>", "project root", process.cwd()).action(async (message, opts) => {
13434
- const root = path47.resolve(opts.dir ?? process.cwd());
13435
- const paths = resolveHaivePaths42(findProjectRoot46(root));
13781
+ const root = path48.resolve(opts.dir ?? process.cwd());
13782
+ const paths = resolveHaivePaths44(findProjectRoot48(root));
13436
13783
  const raw = opts.kind ?? "note";
13437
13784
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
13438
13785
  await appendRuntimeJournalEntry3(paths, { kind, message });
13439
- ui.success(`Appended to ${path47.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
13786
+ ui.success(`Appended to ${path48.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
13440
13787
  });
13441
13788
  journal.command("tail").description("Print the last N entries from the runtime session journal as JSON").option("-n, --limit <n>", "number of lines", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
13442
- const root = path47.resolve(opts.dir ?? process.cwd());
13443
- const paths = resolveHaivePaths42(findProjectRoot46(root));
13789
+ const root = path48.resolve(opts.dir ?? process.cwd());
13790
+ const paths = resolveHaivePaths44(findProjectRoot48(root));
13444
13791
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
13445
- if (!existsSync66(paths.haiveDir)) {
13792
+ if (!existsSync69(paths.haiveDir)) {
13446
13793
  ui.error("No .ai/ \u2014 run `haive init` first.");
13447
13794
  process.exitCode = 1;
13448
13795
  return;
@@ -13457,13 +13804,13 @@ function registerRuntime(program2) {
13457
13804
  }
13458
13805
 
13459
13806
  // src/commands/memory-timeline.ts
13460
- import { existsSync as existsSync67 } from "fs";
13461
- import path48 from "path";
13807
+ import { existsSync as existsSync70 } from "fs";
13808
+ import path49 from "path";
13462
13809
  import "commander";
13463
13810
  import {
13464
13811
  collectTimelineEntries as collectTimelineEntries2,
13465
- findProjectRoot as findProjectRoot47,
13466
- resolveHaivePaths as resolveHaivePaths43
13812
+ findProjectRoot as findProjectRoot49,
13813
+ resolveHaivePaths as resolveHaivePaths45
13467
13814
  } from "@hiveai/core";
13468
13815
  function registerMemoryTimeline(memory2) {
13469
13816
  memory2.command("timeline").description(
@@ -13474,15 +13821,15 @@ function registerMemoryTimeline(memory2) {
13474
13821
  process.exitCode = 1;
13475
13822
  return;
13476
13823
  }
13477
- const root = path48.resolve(opts.dir ?? process.cwd());
13478
- const paths = resolveHaivePaths43(findProjectRoot47(root));
13479
- if (!existsSync67(paths.memoriesDir)) {
13824
+ const root = path49.resolve(opts.dir ?? process.cwd());
13825
+ const paths = resolveHaivePaths45(findProjectRoot49(root));
13826
+ if (!existsSync70(paths.memoriesDir)) {
13480
13827
  ui.error("No memories \u2014 run `haive init`.");
13481
13828
  process.exitCode = 1;
13482
13829
  return;
13483
13830
  }
13484
13831
  const limit = Math.min(100, Math.max(1, parseInt(opts.limit, 10) || 30));
13485
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
13832
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
13486
13833
  const { entries, notice } = collectTimelineEntries2(all, {
13487
13834
  memoryId: opts.id,
13488
13835
  topic: opts.topic,
@@ -13494,14 +13841,14 @@ function registerMemoryTimeline(memory2) {
13494
13841
  }
13495
13842
 
13496
13843
  // src/commands/memory-conflict-candidates.ts
13497
- import { existsSync as existsSync68 } from "fs";
13498
- import path49 from "path";
13844
+ import { existsSync as existsSync71 } from "fs";
13845
+ import path50 from "path";
13499
13846
  import "commander";
13500
13847
  import {
13501
13848
  findLexicalConflictPairs as findLexicalConflictPairs2,
13502
13849
  findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
13503
- findProjectRoot as findProjectRoot48,
13504
- resolveHaivePaths as resolveHaivePaths44
13850
+ findProjectRoot as findProjectRoot50,
13851
+ resolveHaivePaths as resolveHaivePaths46
13505
13852
  } from "@hiveai/core";
13506
13853
  function parseTypes(csv) {
13507
13854
  const allowed = ["decision", "architecture", "convention", "gotcha"];
@@ -13517,9 +13864,9 @@ function registerMemoryConflictCandidates(memory2) {
13517
13864
  "decision,architecture,convention,gotcha (lexical scan)",
13518
13865
  "decision,architecture"
13519
13866
  ).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
13520
- const root = path49.resolve(opts.dir ?? process.cwd());
13521
- const paths = resolveHaivePaths44(findProjectRoot48(root));
13522
- if (!existsSync68(paths.memoriesDir)) {
13867
+ const root = path50.resolve(opts.dir ?? process.cwd());
13868
+ const paths = resolveHaivePaths46(findProjectRoot50(root));
13869
+ if (!existsSync71(paths.memoriesDir)) {
13523
13870
  ui.error("No memories \u2014 run `haive init`.");
13524
13871
  process.exitCode = 1;
13525
13872
  return;
@@ -13529,7 +13876,7 @@ function registerMemoryConflictCandidates(memory2) {
13529
13876
  const maxPairs = Math.min(100, Math.max(1, parseInt(opts.maxPairs, 10) || 20));
13530
13877
  const maxScan = Math.min(2e3, Math.max(1, parseInt(opts.maxScan, 10) || 500));
13531
13878
  const maxTopicPairs = Math.min(100, Math.max(1, parseInt(opts.maxTopicPairs, 10) || 20));
13532
- const all = await loadMemoriesFromDir25(paths.memoriesDir);
13879
+ const all = await loadMemoriesFromDir26(paths.memoriesDir);
13533
13880
  const lexical = findLexicalConflictPairs2(all, {
13534
13881
  sinceDays,
13535
13882
  types: parseTypes(opts.types),
@@ -13555,21 +13902,21 @@ function registerMemoryConflictCandidates(memory2) {
13555
13902
 
13556
13903
  // src/commands/enforce.ts
13557
13904
  import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
13558
- import { existsSync as existsSync69, statSync as statSync3 } from "fs";
13559
- import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
13560
- import path50 from "path";
13905
+ import { existsSync as existsSync73, statSync as statSync3 } from "fs";
13906
+ import { chmod as chmod2, mkdir as mkdir19, readFile as readFile23, readdir as readdir6, rm as rm3, writeFile as writeFile34 } from "fs/promises";
13907
+ import path51 from "path";
13561
13908
  import "commander";
13562
13909
  import {
13563
13910
  antiPatternGateParams as antiPatternGateParams2,
13564
- findProjectRoot as findProjectRoot49,
13911
+ findProjectRoot as findProjectRoot51,
13565
13912
  hasRecentBriefingMarker as hasRecentBriefingMarker2,
13566
13913
  isFreshIsoDate,
13567
13914
  loadConfig as loadConfig13,
13568
- loadMemoriesFromDir as loadMemoriesFromDir36,
13915
+ loadMemoriesFromDir as loadMemoriesFromDir37,
13569
13916
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
13570
13917
  readRecentBriefingMarker,
13571
13918
  resolveBriefingBudget as resolveBriefingBudget3,
13572
- resolveHaivePaths as resolveHaivePaths45,
13919
+ resolveHaivePaths as resolveHaivePaths47,
13573
13920
  saveConfig as saveConfig4,
13574
13921
  SESSION_RECAP_TTL_MS,
13575
13922
  verifyAnchor as verifyAnchor4,
@@ -13582,8 +13929,8 @@ function registerEnforce(program2) {
13582
13929
  "Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
13583
13930
  );
13584
13931
  enforce.command("install").description("Install hAIve 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) => {
13585
- const root = findProjectRoot49(opts.dir);
13586
- const paths = resolveHaivePaths45(root);
13932
+ const root = findProjectRoot51(opts.dir);
13933
+ const paths = resolveHaivePaths47(root);
13587
13934
  await mkdir19(paths.haiveDir, { recursive: true });
13588
13935
  const current = await loadConfig13(paths);
13589
13936
  await saveConfig4(paths, {
@@ -13608,7 +13955,7 @@ function registerEnforce(program2) {
13608
13955
  if (opts.claude !== false) {
13609
13956
  try {
13610
13957
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
13611
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path50.relative(root, result.settingsPath)})`);
13958
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path51.relative(root, result.settingsPath)})`);
13612
13959
  } catch (err) {
13613
13960
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
13614
13961
  }
@@ -13627,21 +13974,21 @@ function registerEnforce(program2) {
13627
13974
  if (report.should_block) process.exit(2);
13628
13975
  });
13629
13976
  enforce.command("cleanup").description("Remove generated hAIve runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
13630
- const root = findProjectRoot49(opts.dir);
13631
- const paths = resolveHaivePaths45(root);
13632
- const cacheDir = path50.join(paths.haiveDir, ".cache");
13633
- if (existsSync69(cacheDir)) {
13634
- if (opts.dryRun) ui.info(`would clean ${path50.relative(root, cacheDir)} (preserving .gitignore)`);
13977
+ const root = findProjectRoot51(opts.dir);
13978
+ const paths = resolveHaivePaths47(root);
13979
+ const cacheDir = path51.join(paths.haiveDir, ".cache");
13980
+ if (existsSync73(cacheDir)) {
13981
+ if (opts.dryRun) ui.info(`would clean ${path51.relative(root, cacheDir)} (preserving .gitignore)`);
13635
13982
  else {
13636
13983
  const removed = await cleanupCacheDir(cacheDir);
13637
- ui.success(`cleaned ${path50.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13984
+ ui.success(`cleaned ${path51.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13638
13985
  }
13639
13986
  }
13640
- if (existsSync69(paths.runtimeDir)) {
13641
- if (opts.dryRun) ui.info(`would clean ${path50.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
13987
+ if (existsSync73(paths.runtimeDir)) {
13988
+ if (opts.dryRun) ui.info(`would clean ${path51.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
13642
13989
  else {
13643
13990
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
13644
- ui.success(`cleaned ${path50.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13991
+ ui.success(`cleaned ${path51.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13645
13992
  }
13646
13993
  }
13647
13994
  });
@@ -13661,8 +14008,8 @@ function registerEnforce(program2) {
13661
14008
  const payload = await readHookPayload();
13662
14009
  const root = resolveRoot(opts.dir, payload);
13663
14010
  if (!root) return;
13664
- const paths = resolveHaivePaths45(root);
13665
- if (!existsSync69(paths.haiveDir)) return;
14011
+ const paths = resolveHaivePaths47(root);
14012
+ if (!existsSync73(paths.haiveDir)) return;
13666
14013
  await mkdir19(paths.runtimeDir, { recursive: true });
13667
14014
  const sessionId = opts.sessionId ?? payload.session_id;
13668
14015
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
@@ -13724,8 +14071,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13724
14071
  const payload = await readHookPayload();
13725
14072
  const root = resolveRoot(opts.dir, payload);
13726
14073
  if (!root) return;
13727
- const paths = resolveHaivePaths45(root);
13728
- if (!existsSync69(paths.haiveDir)) return;
14074
+ const paths = resolveHaivePaths47(root);
14075
+ if (!existsSync73(paths.haiveDir)) return;
13729
14076
  if (!isWriteLikeTool(payload)) return;
13730
14077
  const ok = await hasRecentBriefingMarker2(paths, payload.session_id);
13731
14078
  if (ok) {
@@ -13767,9 +14114,9 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13767
14114
  });
13768
14115
  }
13769
14116
  async function buildFinishReport(dir) {
13770
- const root = findProjectRoot49(dir);
13771
- const paths = resolveHaivePaths45(root);
13772
- const initialized = existsSync69(paths.haiveDir);
14117
+ const root = findProjectRoot51(dir);
14118
+ const paths = resolveHaivePaths47(root);
14119
+ const initialized = existsSync73(paths.haiveDir);
13773
14120
  const config = initialized ? await loadConfig13(paths) : {};
13774
14121
  const mode = config.enforcement?.mode ?? "strict";
13775
14122
  const findings = [];
@@ -13963,9 +14310,9 @@ function finishReport(root, initialized, mode, findings, config) {
13963
14310
  });
13964
14311
  }
13965
14312
  async function runWithEnforcement(command, args, opts) {
13966
- const root = findProjectRoot49(opts.dir);
13967
- const paths = resolveHaivePaths45(root);
13968
- if (!existsSync69(paths.haiveDir)) {
14313
+ const root = findProjectRoot51(opts.dir);
14314
+ const paths = resolveHaivePaths47(root);
14315
+ if (!existsSync73(paths.haiveDir)) {
13969
14316
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
13970
14317
  process.exit(1);
13971
14318
  }
@@ -13984,7 +14331,7 @@ async function runWithEnforcement(command, args, opts) {
13984
14331
  process.exit(2);
13985
14332
  }
13986
14333
  ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
13987
- ui.info(`Briefing written to ${path50.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
14334
+ ui.info(`Briefing written to ${path51.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
13988
14335
  const child = spawn6(command, args, {
13989
14336
  cwd: root,
13990
14337
  stdio: "inherit",
@@ -14034,9 +14381,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
14034
14381
  source: "haive-run",
14035
14382
  memoryIds: briefing.memories.map((m) => m.id)
14036
14383
  });
14037
- const dir = path50.join(paths.runtimeDir, "enforcement", "briefings");
14384
+ const dir = path51.join(paths.runtimeDir, "enforcement", "briefings");
14038
14385
  await mkdir19(dir, { recursive: true });
14039
- const file = path50.join(dir, `${sessionId}.md`);
14386
+ const file = path51.join(dir, `${sessionId}.md`);
14040
14387
  const parts = [
14041
14388
  "# hAIve Briefing",
14042
14389
  "",
@@ -14054,13 +14401,13 @@ async function writeWrapperBriefing(paths, sessionId, task) {
14054
14401
  if (briefing.setup_warnings.length > 0) {
14055
14402
  parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
14056
14403
  }
14057
- await writeFile33(file, parts.join("\n") + "\n", "utf8");
14404
+ await writeFile34(file, parts.join("\n") + "\n", "utf8");
14058
14405
  return file;
14059
14406
  }
14060
14407
  async function buildEnforcementReport(dir, stage, sessionId) {
14061
- const root = findProjectRoot49(dir);
14062
- const paths = resolveHaivePaths45(root);
14063
- const initialized = existsSync69(paths.haiveDir);
14408
+ const root = findProjectRoot51(dir);
14409
+ const paths = resolveHaivePaths47(root);
14410
+ const initialized = existsSync73(paths.haiveDir);
14064
14411
  const config = initialized ? await loadConfig13(paths) : {};
14065
14412
  if (initialized) await applyLightweightRepairs(root, paths);
14066
14413
  const mode = config.enforcement?.mode ?? "strict";
@@ -14091,7 +14438,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
14091
14438
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
14092
14439
  });
14093
14440
  }
14094
- findings.push(...await inspectIntegrationVersions(root, "0.10.9"));
14441
+ findings.push(...await inspectIntegrationVersions(root, "0.11.0"));
14095
14442
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
14096
14443
  const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
14097
14444
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -14161,8 +14508,8 @@ function withCategories(report) {
14161
14508
  };
14162
14509
  }
14163
14510
  async function hasRecentSessionRecap(paths) {
14164
- if (!existsSync69(paths.memoriesDir)) return false;
14165
- const all = await loadMemoriesFromDir36(paths.memoriesDir);
14511
+ if (!existsSync73(paths.memoriesDir)) return false;
14512
+ const all = await loadMemoriesFromDir37(paths.memoriesDir);
14166
14513
  return all.some(({ memory: memory2 }) => {
14167
14514
  const fm = memory2.frontmatter;
14168
14515
  const freshnessDate = fm.verified_at ?? fm.created_at;
@@ -14170,8 +14517,8 @@ async function hasRecentSessionRecap(paths) {
14170
14517
  });
14171
14518
  }
14172
14519
  async function verifyMemoryPolicy(paths, config) {
14173
- if (!existsSync69(paths.memoriesDir)) return [];
14174
- const all = await loadMemoriesFromDir36(paths.memoriesDir);
14520
+ if (!existsSync73(paths.memoriesDir)) return [];
14521
+ const all = await loadMemoriesFromDir37(paths.memoriesDir);
14175
14522
  const findings = [];
14176
14523
  const staleImportant = [];
14177
14524
  let verified = 0;
@@ -14208,12 +14555,12 @@ async function verifyMemoryPolicy(paths, config) {
14208
14555
  return findings;
14209
14556
  }
14210
14557
  async function verifyDecisionCoverage(paths, stage, sessionId) {
14211
- if (!existsSync69(paths.memoriesDir)) return [];
14558
+ if (!existsSync73(paths.memoriesDir)) return [];
14212
14559
  const changedFiles = await getChangedFiles(paths.root, stage);
14213
14560
  if (changedFiles.length === 0) {
14214
14561
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
14215
14562
  }
14216
- const all = await loadMemoriesFromDir36(paths.memoriesDir);
14563
+ const all = await loadMemoriesFromDir37(paths.memoriesDir);
14217
14564
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention"]);
14218
14565
  const relevant = all.map(({ memory: memory2 }) => memory2).filter((memory2) => {
14219
14566
  const fm = memory2.frontmatter;
@@ -14313,16 +14660,16 @@ async function cleanupRuntimeDir(runtimeDir) {
14313
14660
  for (const entry of entries) {
14314
14661
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
14315
14662
  if (entry.name === "enforcement") {
14316
- removed += await cleanupEnforcementDir(path50.join(runtimeDir, entry.name));
14663
+ removed += await cleanupEnforcementDir(path51.join(runtimeDir, entry.name));
14317
14664
  continue;
14318
14665
  }
14319
- await rm3(path50.join(runtimeDir, entry.name), { recursive: true, force: true });
14666
+ await rm3(path51.join(runtimeDir, entry.name), { recursive: true, force: true });
14320
14667
  removed++;
14321
14668
  }
14322
- await writeFile33(path50.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
14323
- if (!existsSync69(path50.join(runtimeDir, "README.md"))) {
14324
- await writeFile33(
14325
- path50.join(runtimeDir, "README.md"),
14669
+ await writeFile34(path51.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
14670
+ if (!existsSync73(path51.join(runtimeDir, "README.md"))) {
14671
+ await writeFile34(
14672
+ path51.join(runtimeDir, "README.md"),
14326
14673
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
14327
14674
  "utf8"
14328
14675
  );
@@ -14335,10 +14682,10 @@ async function cleanupCacheDir(cacheDir) {
14335
14682
  const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
14336
14683
  for (const entry of entries) {
14337
14684
  if (entry.name === ".gitignore") continue;
14338
- await rm3(path50.join(cacheDir, entry.name), { recursive: true, force: true });
14685
+ await rm3(path51.join(cacheDir, entry.name), { recursive: true, force: true });
14339
14686
  removed++;
14340
14687
  }
14341
- await writeFile33(path50.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
14688
+ await writeFile34(path51.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
14342
14689
  return removed;
14343
14690
  }
14344
14691
  async function cleanupEnforcementDir(enforcementDir) {
@@ -14346,7 +14693,7 @@ async function cleanupEnforcementDir(enforcementDir) {
14346
14693
  const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
14347
14694
  for (const entry of entries) {
14348
14695
  if (entry.name === "briefings") continue;
14349
- await rm3(path50.join(enforcementDir, entry.name), { recursive: true, force: true });
14696
+ await rm3(path51.join(enforcementDir, entry.name), { recursive: true, force: true });
14350
14697
  removed++;
14351
14698
  }
14352
14699
  return removed;
@@ -14362,9 +14709,9 @@ async function inspectIntegrationVersions(root, expectedVersion) {
14362
14709
  ];
14363
14710
  const findings = [];
14364
14711
  for (const rel of files) {
14365
- const file = path50.join(root, rel);
14366
- if (!existsSync69(file)) continue;
14367
- const text = await readFile21(file, "utf8").catch(() => "");
14712
+ const file = path51.join(root, rel);
14713
+ if (!existsSync73(file)) continue;
14714
+ const text = await readFile23(file, "utf8").catch(() => "");
14368
14715
  for (const bin of extractAbsoluteHaiveBins2(text)) {
14369
14716
  const version = versionForBinary2(bin);
14370
14717
  if (!version) {
@@ -14473,9 +14820,9 @@ async function resolveCiDiffRange(root) {
14473
14820
  }
14474
14821
  async function resolveGithubEventRange(root) {
14475
14822
  const eventPath = process.env.GITHUB_EVENT_PATH;
14476
- if (!eventPath || !existsSync69(eventPath)) return null;
14823
+ if (!eventPath || !existsSync73(eventPath)) return null;
14477
14824
  try {
14478
- const event = JSON.parse(await readFile21(eventPath, "utf8"));
14825
+ const event = JSON.parse(await readFile23(eventPath, "utf8"));
14479
14826
  const prBase = cleanGitSha(event.pull_request?.base?.sha);
14480
14827
  const prHead = cleanGitSha(event.pull_request?.head?.sha ?? event.after ?? process.env.GITHUB_SHA) ?? "HEAD";
14481
14828
  if (prBase && await gitCommitExists(root, prBase)) return `${prBase}...${prHead}`;
@@ -14592,7 +14939,7 @@ async function inspectReleaseVersionState(root, upstream) {
14592
14939
  }
14593
14940
  async function readPackageVersion(root, relPath) {
14594
14941
  try {
14595
- const data = JSON.parse(await readFile21(path50.join(root, relPath), "utf8"));
14942
+ const data = JSON.parse(await readFile23(path51.join(root, relPath), "utf8"));
14596
14943
  return typeof data.version === "string" ? data.version : void 0;
14597
14944
  } catch {
14598
14945
  return void 0;
@@ -14653,8 +15000,8 @@ function buildScore(findings, threshold = 80) {
14653
15000
  };
14654
15001
  }
14655
15002
  async function installGitEnforcement(root) {
14656
- const hooksDir = path50.join(root, ".git", "hooks");
14657
- if (!existsSync69(path50.join(root, ".git"))) {
15003
+ const hooksDir = path51.join(root, ".git", "hooks");
15004
+ if (!existsSync73(path51.join(root, ".git"))) {
14658
15005
  ui.warn("No .git directory found; git enforcement hooks skipped.");
14659
15006
  return;
14660
15007
  }
@@ -14676,31 +15023,31 @@ haive enforce check --stage pre-push --dir . || exit $?
14676
15023
  }
14677
15024
  ];
14678
15025
  for (const hook of hooks) {
14679
- const file = path50.join(hooksDir, hook.name);
14680
- if (existsSync69(file)) {
14681
- const current = await readFile21(file, "utf8").catch(() => "");
15026
+ const file = path51.join(hooksDir, hook.name);
15027
+ if (existsSync73(file)) {
15028
+ const current = await readFile23(file, "utf8").catch(() => "");
14682
15029
  if (current.includes(ENFORCE_HOOK_MARKER)) {
14683
- await writeFile33(file, hook.body, "utf8");
15030
+ await writeFile34(file, hook.body, "utf8");
14684
15031
  } else {
14685
- await writeFile33(file, `${current.trimEnd()}
15032
+ await writeFile34(file, `${current.trimEnd()}
14686
15033
 
14687
15034
  ${hook.body}`, "utf8");
14688
15035
  }
14689
15036
  } else {
14690
- await writeFile33(file, hook.body, "utf8");
15037
+ await writeFile34(file, hook.body, "utf8");
14691
15038
  }
14692
15039
  await chmod2(file, 493);
14693
15040
  }
14694
15041
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
14695
15042
  }
14696
15043
  async function installCiEnforcement(root) {
14697
- const workflowPath = path50.join(root, ".github", "workflows", "haive-enforcement.yml");
14698
- await mkdir19(path50.dirname(workflowPath), { recursive: true });
14699
- if (existsSync69(workflowPath)) {
15044
+ const workflowPath = path51.join(root, ".github", "workflows", "haive-enforcement.yml");
15045
+ await mkdir19(path51.dirname(workflowPath), { recursive: true });
15046
+ if (existsSync73(workflowPath)) {
14700
15047
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
14701
15048
  return;
14702
15049
  }
14703
- await writeFile33(workflowPath, `name: haive-enforcement
15050
+ await writeFile34(workflowPath, `name: haive-enforcement
14704
15051
 
14705
15052
  on:
14706
15053
  pull_request:
@@ -14727,7 +15074,7 @@ jobs:
14727
15074
  HAIVE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
14728
15075
  run: haive enforce ci
14729
15076
  `, "utf8");
14730
- ui.success(`Created ${path50.relative(root, workflowPath)}`);
15077
+ ui.success(`Created ${path51.relative(root, workflowPath)}`);
14731
15078
  }
14732
15079
  function printReport(report, json, explain = false) {
14733
15080
  if (json) {
@@ -14787,7 +15134,7 @@ async function readHookPayload() {
14787
15134
  }
14788
15135
  function resolveRoot(dir, payload) {
14789
15136
  try {
14790
- return findProjectRoot49(dir ?? payload.cwd);
15137
+ return findProjectRoot51(dir ?? payload.cwd);
14791
15138
  } catch {
14792
15139
  return null;
14793
15140
  }
@@ -14824,15 +15171,15 @@ function extractToolPaths(payload, root) {
14824
15171
  }
14825
15172
  function normalizeToolPath(file, root) {
14826
15173
  const normalized = file.replace(/\\/g, "/");
14827
- if (!path50.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
14828
- return path50.relative(root, normalized).replace(/\\/g, "/");
15174
+ if (!path51.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
15175
+ return path51.relative(root, normalized).replace(/\\/g, "/");
14829
15176
  }
14830
15177
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
14831
- if (!existsSync69(paths.memoriesDir)) return [];
15178
+ if (!existsSync73(paths.memoriesDir)) return [];
14832
15179
  const marker = await readRecentBriefingMarker(paths, sessionId);
14833
15180
  const consulted = new Set(marker?.memory_ids ?? []);
14834
15181
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
14835
- const all = await loadMemoriesFromDir36(paths.memoriesDir);
15182
+ const all = await loadMemoriesFromDir37(paths.memoriesDir);
14836
15183
  return all.filter(({ memory: memory2 }) => {
14837
15184
  const fm = memory2.frontmatter;
14838
15185
  if (!policyTypes.has(fm.type)) return false;
@@ -14902,16 +15249,16 @@ function registerRun(program2) {
14902
15249
 
14903
15250
  // src/commands/sensors.ts
14904
15251
  import { execFile as execFile2 } from "child_process";
14905
- import { existsSync as existsSync70 } from "fs";
14906
- import { chmod as chmod3, mkdir as mkdir20, readFile as readFile23, writeFile as writeFile34 } from "fs/promises";
14907
- import path51 from "path";
15252
+ import { existsSync as existsSync74 } from "fs";
15253
+ import { chmod as chmod3, mkdir as mkdir20, readFile as readFile24, writeFile as writeFile35 } from "fs/promises";
15254
+ import path53 from "path";
14908
15255
  import { promisify as promisify2 } from "util";
14909
15256
  import "commander";
14910
15257
  import {
14911
- findProjectRoot as findProjectRoot50,
15258
+ findProjectRoot as findProjectRoot52,
14912
15259
  isRetiredMemory as isRetiredMemory3,
14913
- loadMemoriesFromDir as loadMemoriesFromDir37,
14914
- resolveHaivePaths as resolveHaivePaths46,
15260
+ loadMemoriesFromDir as loadMemoriesFromDir38,
15261
+ resolveHaivePaths as resolveHaivePaths48,
14915
15262
  runSensors as runSensors2,
14916
15263
  sensorTargetsFromDiff as sensorTargetsFromDiff2,
14917
15264
  serializeMemory as serializeMemory26
@@ -14920,8 +15267,8 @@ var exec2 = promisify2(execFile2);
14920
15267
  function registerSensors(program2) {
14921
15268
  const sensors = program2.command("sensors").description("Operate executable sensors derived from hAIve memories");
14922
15269
  sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
14923
- const root = findProjectRoot50(opts.dir);
14924
- const paths = resolveHaivePaths46(root);
15270
+ const root = findProjectRoot52(opts.dir);
15271
+ const paths = resolveHaivePaths48(root);
14925
15272
  const rows = await sensorRows(paths);
14926
15273
  if (opts.json) {
14927
15274
  console.log(JSON.stringify(rows, null, 2));
@@ -14941,10 +15288,10 @@ function registerSensors(program2) {
14941
15288
  }
14942
15289
  });
14943
15290
  sensors.command("check").description("Run regex sensors against a diff; defaults to `git diff --cached`").option("--diff-file <path>", "read unified diff from a file instead of staged changes").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
14944
- const root = findProjectRoot50(opts.dir);
14945
- const paths = resolveHaivePaths46(root);
15291
+ const root = findProjectRoot52(opts.dir);
15292
+ const paths = resolveHaivePaths48(root);
14946
15293
  const memories = await runnableSensorMemories(paths);
14947
- const diff = opts.diffFile ? await readFile23(path51.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
15294
+ const diff = opts.diffFile ? await readFile24(path53.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
14948
15295
  const targets = sensorTargetsFromDiff2(diff);
14949
15296
  const hits = runSensors2(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
14950
15297
  const output = {
@@ -14983,9 +15330,9 @@ function registerSensors(program2) {
14983
15330
  process.exitCode = 1;
14984
15331
  return;
14985
15332
  }
14986
- const root = findProjectRoot50(opts.dir);
14987
- const paths = resolveHaivePaths46(root);
14988
- const loaded = existsSync70(paths.memoriesDir) ? await loadMemoriesFromDir37(paths.memoriesDir) : [];
15333
+ const root = findProjectRoot52(opts.dir);
15334
+ const paths = resolveHaivePaths48(root);
15335
+ const loaded = existsSync74(paths.memoriesDir) ? await loadMemoriesFromDir38(paths.memoriesDir) : [];
14989
15336
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
14990
15337
  if (!found) {
14991
15338
  ui.error(`No memory found with id ${id}`);
@@ -15005,7 +15352,7 @@ function registerSensors(program2) {
15005
15352
  },
15006
15353
  body: found.memory.body
15007
15354
  };
15008
- await writeFile34(found.filePath, serializeMemory26(next), "utf8");
15355
+ await writeFile35(found.filePath, serializeMemory26(next), "utf8");
15009
15356
  ui.success(`Updated ${id}: sensor severity=${severity}`);
15010
15357
  if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
15011
15358
  ui.info(`message=${sensor.message}`);
@@ -15017,16 +15364,16 @@ function registerSensors(program2) {
15017
15364
  process.exitCode = 1;
15018
15365
  return;
15019
15366
  }
15020
- const root = findProjectRoot50(opts.dir);
15021
- const paths = resolveHaivePaths46(root);
15367
+ const root = findProjectRoot52(opts.dir);
15368
+ const paths = resolveHaivePaths48(root);
15022
15369
  const rows = await sensorRows(paths);
15023
- const outDir = path51.resolve(root, opts.outDir ?? ".ai/generated");
15370
+ const outDir = path53.resolve(root, opts.outDir ?? ".ai/generated");
15024
15371
  await mkdir20(outDir, { recursive: true });
15025
- const outPath = path51.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
15372
+ const outPath = path53.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
15026
15373
  const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
15027
- await writeFile34(outPath, content, "utf8");
15374
+ await writeFile35(outPath, content, "utf8");
15028
15375
  if (format === "grep") await chmod3(outPath, 493);
15029
- ui.success(`Exported ${rows.length} sensor(s): ${path51.relative(root, outPath)}`);
15376
+ ui.success(`Exported ${rows.length} sensor(s): ${path53.relative(root, outPath)}`);
15030
15377
  });
15031
15378
  }
15032
15379
  async function sensorRows(paths) {
@@ -15047,8 +15394,8 @@ async function sensorRows(paths) {
15047
15394
  });
15048
15395
  }
15049
15396
  async function runnableSensorMemories(paths, regexOnly = true) {
15050
- if (!existsSync70(paths.memoriesDir)) return [];
15051
- const loaded = await loadMemoriesFromDir37(paths.memoriesDir);
15397
+ if (!existsSync74(paths.memoriesDir)) return [];
15398
+ const loaded = await loadMemoriesFromDir38(paths.memoriesDir);
15052
15399
  return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
15053
15400
  const sensor = memory2.frontmatter.sensor;
15054
15401
  if (!sensor) return false;
@@ -15089,8 +15436,8 @@ function shellQuote(value) {
15089
15436
  }
15090
15437
 
15091
15438
  // src/index.ts
15092
- var program = new Command53();
15093
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.10.9").option("--advanced", "show maintenance and experimental commands in help");
15439
+ var program = new Command55();
15440
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.11.0").option("--advanced", "show maintenance and experimental commands in help");
15094
15441
  registerInit(program);
15095
15442
  registerWelcome(program);
15096
15443
  registerResolveProject(program);
@@ -15114,6 +15461,7 @@ registerMemoryQuery(memory);
15114
15461
  registerMemoryPromote(memory);
15115
15462
  registerMemoryVerify(memory);
15116
15463
  registerMemoryStats(memory);
15464
+ registerMemoryImpact(memory);
15117
15465
  registerMemoryReject(memory);
15118
15466
  registerMemoryAutoPromote(memory);
15119
15467
  registerMemoryForFiles(memory);
@@ -15144,6 +15492,7 @@ registerHub(program);
15144
15492
  registerStats(program);
15145
15493
  registerBench(program);
15146
15494
  registerBenchmark(program);
15495
+ registerEval(program);
15147
15496
  registerDoctor(program);
15148
15497
  registerPlayback(program);
15149
15498
  registerPrecommit(program);