@hiveai/cli 0.10.5 → 0.10.8

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
@@ -3746,9 +3746,8 @@ import { mkdir as mkdir52, writeFile as writeFile92, rm } from "fs/promises";
3746
3746
  import { existsSync as existsSync16 } from "fs";
3747
3747
  import path72 from "path";
3748
3748
  import { execSync } from "child_process";
3749
- import { readFile as readFile32, readdir as readdir3, writeFile as writeFile11 } from "fs/promises";
3750
- import { existsSync as existsSync18 } from "fs";
3751
- import path92 from "path";
3749
+ import { readFile as readFile42, writeFile as writeFile11 } from "fs/promises";
3750
+ import { existsSync as existsSync19 } from "fs";
3752
3751
  import {
3753
3752
  allocateBudget,
3754
3753
  DEFAULT_AUTO_PROMOTE_RULE,
@@ -3757,11 +3756,9 @@ import {
3757
3756
  extractActionsBriefBody as extractActionsBriefBody2,
3758
3757
  getUsage as getUsage5,
3759
3758
  inferModulesFromPaths as inferModulesFromPaths2,
3760
- isGlobPath,
3761
- isRetiredMemory,
3762
3759
  isAutoPromoteEligible,
3763
3760
  isDecaying,
3764
- isStackPackSeed as isStackPackSeed2,
3761
+ isRetiredMemory,
3765
3762
  literalMatchesAllTokens as literalMatchesAllTokens22,
3766
3763
  literalMatchesAnyToken as literalMatchesAnyToken22,
3767
3764
  loadCodeMap as loadCodeMap5,
@@ -3769,7 +3766,6 @@ import {
3769
3766
  loadMemoriesFromDir as loadMemoriesFromDir13,
3770
3767
  loadUsageIndex as loadUsageIndex7,
3771
3768
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths22,
3772
- pathsOverlap,
3773
3769
  queryCodeMap as queryCodeMap2,
3774
3770
  resolveBriefingBudget as resolveBriefingBudget2,
3775
3771
  serializeMemory as serializeMemory9,
@@ -3781,17 +3777,21 @@ import {
3781
3777
  writeBriefingMarker as writeBriefingMarker2
3782
3778
  } from "@hiveai/core";
3783
3779
  import { z as z17 } from "zod";
3780
+ import { readdir as readdir3, readFile as readFile32 } from "fs/promises";
3781
+ import { existsSync as existsSync18 } from "fs";
3782
+ import path92 from "path";
3783
+ import { isGlobPath, isStackPackSeed as isStackPackSeed2, pathsOverlap } from "@hiveai/core";
3784
3784
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
3785
3785
  import { z as z18 } from "zod";
3786
- import { existsSync as existsSync19 } from "fs";
3786
+ import { existsSync as existsSync20 } from "fs";
3787
3787
  import { loadMemoriesFromDir as loadMemoriesFromDir14 } from "@hiveai/core";
3788
3788
  import { z as z19 } from "zod";
3789
- import { existsSync as existsSync20 } from "fs";
3789
+ import { existsSync as existsSync21 } from "fs";
3790
3790
  import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
3791
3791
  import { z as z20 } from "zod";
3792
3792
  import { z as z21 } from "zod";
3793
3793
  import { z as z22 } from "zod";
3794
- import { existsSync as existsSync21 } from "fs";
3794
+ import { existsSync as existsSync222 } from "fs";
3795
3795
  import { spawn } from "child_process";
3796
3796
  import path102 from "path";
3797
3797
  import {
@@ -3803,7 +3803,7 @@ import {
3803
3803
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
3804
3804
  } from "@hiveai/core";
3805
3805
  import { z as z23 } from "zod";
3806
- import { existsSync as existsSync222 } from "fs";
3806
+ import { existsSync as existsSync23 } from "fs";
3807
3807
  import {
3808
3808
  addedLinesFromDiff,
3809
3809
  deriveConfidence as deriveConfidence6,
@@ -3818,13 +3818,13 @@ import {
3818
3818
  tokenizeQuery as tokenizeQuery3
3819
3819
  } from "@hiveai/core";
3820
3820
  import { z as z24 } from "zod";
3821
- import { existsSync as existsSync23 } from "fs";
3821
+ import { existsSync as existsSync24 } from "fs";
3822
3822
  import {
3823
3823
  loadMemoriesFromDir as loadMemoriesFromDir18,
3824
3824
  tokenizeQuery as tokenizeQuery4
3825
3825
  } from "@hiveai/core";
3826
3826
  import { z as z25 } from "zod";
3827
- import { existsSync as existsSync24 } from "fs";
3827
+ import { existsSync as existsSync25 } from "fs";
3828
3828
  import { spawn as spawn2 } from "child_process";
3829
3829
  import {
3830
3830
  deriveConfidence as deriveConfidence7,
@@ -3834,7 +3834,7 @@ import {
3834
3834
  pathsOverlap as singlePathsOverlap
3835
3835
  } from "@hiveai/core";
3836
3836
  import { z as z26 } from "zod";
3837
- import { existsSync as existsSync25 } from "fs";
3837
+ import { existsSync as existsSync26 } from "fs";
3838
3838
  import {
3839
3839
  deriveConfidence as deriveConfidence8,
3840
3840
  getUsage as getUsage9,
@@ -3846,7 +3846,7 @@ import {
3846
3846
  import { z as z27 } from "zod";
3847
3847
  import { z as z28 } from "zod";
3848
3848
  import { mkdir as mkdir72, writeFile as writeFile12 } from "fs/promises";
3849
- import { existsSync as existsSync26 } from "fs";
3849
+ import { existsSync as existsSync27 } from "fs";
3850
3850
  import path112 from "path";
3851
3851
  import { execSync as execSync2 } from "child_process";
3852
3852
  import {
@@ -3856,7 +3856,7 @@ import {
3856
3856
  serializeMemory as serializeMemory10
3857
3857
  } from "@hiveai/core";
3858
3858
  import { z as z29 } from "zod";
3859
- import { existsSync as existsSync27 } from "fs";
3859
+ import { existsSync as existsSync28 } from "fs";
3860
3860
  import {
3861
3861
  findLexicalConflictPairs,
3862
3862
  findTopicStatusConflictPairs,
@@ -3867,7 +3867,7 @@ import { resolveProjectInfo } from "@hiveai/core";
3867
3867
  import { z as z31 } from "zod";
3868
3868
  import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3869
3869
  import { z as z32 } from "zod";
3870
- import { existsSync as existsSync28 } from "fs";
3870
+ import { existsSync as existsSync29 } from "fs";
3871
3871
  import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir222 } from "@hiveai/core";
3872
3872
  import { z as z33 } from "zod";
3873
3873
  import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
@@ -3877,7 +3877,7 @@ import { z as z35 } from "zod";
3877
3877
  import { z as z36 } from "zod";
3878
3878
  import { z as z37 } from "zod";
3879
3879
  import { z as z38 } from "zod";
3880
- import { loadConfigSync } from "@hiveai/core";
3880
+ import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
3881
3881
  function createContext(options = {}) {
3882
3882
  const env = options.env ?? process.env;
3883
3883
  const cwd = options.cwd ?? process.cwd();
@@ -5108,6 +5108,144 @@ async function memSessionEnd(input, ctx) {
5108
5108
  revision_count: 0
5109
5109
  };
5110
5110
  }
5111
+ function compactSummary(body) {
5112
+ for (const line of body.split("\n")) {
5113
+ const trimmed = line.replace(/^#+\s*/, "").trim();
5114
+ if (trimmed.length > 0) return trimmed.slice(0, 120);
5115
+ }
5116
+ return body.slice(0, 120);
5117
+ }
5118
+ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
5119
+ const fm = loaded?.memory.frontmatter;
5120
+ const directAnchor = Boolean(
5121
+ fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
5122
+ );
5123
+ const directSymbol = Boolean(
5124
+ fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
5125
+ (sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
5126
+ )
5127
+ );
5128
+ const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
5129
+ const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
5130
+ if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
5131
+ return "must_read";
5132
+ }
5133
+ if (isStackPackSeed2(fm)) {
5134
+ return "background";
5135
+ }
5136
+ if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
5137
+ return "useful";
5138
+ }
5139
+ return "background";
5140
+ }
5141
+ function priorityRank(priority) {
5142
+ return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
5143
+ }
5144
+ function classifyBriefingQuality(memories, context) {
5145
+ const mustRead = memories.filter((m) => m.priority === "must_read").length;
5146
+ const useful = memories.filter((m) => m.priority === "useful").length;
5147
+ const background = memories.filter((m) => m.priority === "background").length;
5148
+ const weakSemantic = memories.filter(
5149
+ (m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
5150
+ ).length;
5151
+ const reasons = [];
5152
+ if (memories.length === 0) reasons.push("no memories matched the task or files");
5153
+ if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
5154
+ if (!context.hasLastSession) reasons.push("no previous session recap");
5155
+ if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
5156
+ if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
5157
+ if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
5158
+ if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
5159
+ if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
5160
+ if (memories.length === 0 || mustRead === 0 && useful === 0) {
5161
+ return { level: "thin", reasons };
5162
+ }
5163
+ if (background > useful + mustRead && background > 2) {
5164
+ return { level: "noisy", reasons };
5165
+ }
5166
+ return { level: "strong", reasons };
5167
+ }
5168
+ function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
5169
+ const why = [];
5170
+ const fm = loaded?.memory.frontmatter;
5171
+ if (memory2.reasons.includes("anchor") && fm) {
5172
+ const matching = fm.anchor.paths.filter(
5173
+ (p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
5174
+ );
5175
+ if (matching.length > 0) {
5176
+ const exact = matching.filter(
5177
+ (p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
5178
+ );
5179
+ const glob = matching.filter((p) => isGlobPath(p));
5180
+ if (exact.length > 0) {
5181
+ why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
5182
+ }
5183
+ if (glob.length > 0) {
5184
+ why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
5185
+ }
5186
+ if (exact.length === 0 && glob.length === 0) {
5187
+ why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
5188
+ }
5189
+ } else if (fm.anchor.paths.length > 0) {
5190
+ why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
5191
+ }
5192
+ if (fm.anchor.symbols.length > 0) {
5193
+ why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
5194
+ }
5195
+ }
5196
+ if (memory2.reasons.includes("symbol") && fm) {
5197
+ why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
5198
+ }
5199
+ if (memory2.reasons.includes("module")) {
5200
+ const moduleHints = [
5201
+ ...memory2.module ? [memory2.module] : [],
5202
+ ...memory2.tags.filter((tag) => inferredModules.includes(tag))
5203
+ ];
5204
+ const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
5205
+ why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
5206
+ }
5207
+ if (memory2.reasons.includes("domain")) {
5208
+ why.push("Matched inferred domain from the target file paths.");
5209
+ }
5210
+ if (memory2.reasons.includes("semantic")) {
5211
+ const score = memory2.semantic_score !== void 0 ? ` score=${Math.round(memory2.semantic_score * 100) / 100}` : "";
5212
+ why.push(`${memory2.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
5213
+ }
5214
+ why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
5215
+ if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
5216
+ if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
5217
+ if (memory2.status === "proposed" || memory2.status === "draft") {
5218
+ why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
5219
+ }
5220
+ return why;
5221
+ }
5222
+ async function trySemanticHits(ctx, task, limit) {
5223
+ let mod;
5224
+ try {
5225
+ mod = await import("@hiveai/embeddings");
5226
+ } catch {
5227
+ return null;
5228
+ }
5229
+ const result = await mod.semanticSearch(ctx.paths, task, { limit });
5230
+ if (!result) return null;
5231
+ return result.hits.map((h) => ({ id: h.id, score: h.score }));
5232
+ }
5233
+ async function loadModuleContexts2(ctx, modules) {
5234
+ if (modules.length === 0) return [];
5235
+ if (!existsSync18(ctx.paths.modulesContextDir)) return [];
5236
+ const available = new Set(
5237
+ (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
5238
+ );
5239
+ const out = [];
5240
+ for (const m of modules) {
5241
+ if (!available.has(m)) continue;
5242
+ const file = path92.join(ctx.paths.modulesContextDir, m, "context.md");
5243
+ if (existsSync18(file)) {
5244
+ out.push({ name: m, content: await readFile32(file, "utf8") });
5245
+ }
5246
+ }
5247
+ return out;
5248
+ }
5111
5249
  var GetBriefingInputSchema = {
5112
5250
  task: z17.string().optional().describe(
5113
5251
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
@@ -5153,7 +5291,7 @@ async function getBriefing(input, ctx) {
5153
5291
  let usage = { version: 1, updated_at: "", by_id: {} };
5154
5292
  let byId = /* @__PURE__ */ new Map();
5155
5293
  let lastSession;
5156
- if (existsSync18(ctx.paths.memoriesDir)) {
5294
+ if (existsSync19(ctx.paths.memoriesDir)) {
5157
5295
  const allLoaded = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
5158
5296
  const recaps = allLoaded.filter(({ memory: memory2 }) => memory2.frontmatter.type === "session_recap").sort(
5159
5297
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
@@ -5238,34 +5376,25 @@ async function getBriefing(input, ctx) {
5238
5376
  if (input.task) {
5239
5377
  const tokens = tokenizeQuery22(input.task);
5240
5378
  const andHits = allMemories.filter((m) => literalMatchesAllTokens22(m.memory, tokens));
5241
- for (const loaded of andHits) {
5242
- addOrUpdate(loaded, "semantic", void 0, "exact");
5243
- }
5379
+ for (const loaded of andHits) addOrUpdate(loaded, "semantic", void 0, "exact");
5244
5380
  if (andHits.length === 0 && tokens.length > 1) {
5245
5381
  for (const loaded of allMemories) {
5246
- if (literalMatchesAnyToken22(loaded.memory, tokens)) {
5247
- addOrUpdate(loaded, "semantic", void 0, "partial");
5248
- }
5382
+ if (literalMatchesAnyToken22(loaded.memory, tokens)) addOrUpdate(loaded, "semantic", void 0, "partial");
5249
5383
  }
5250
5384
  }
5251
5385
  if (semanticHits) {
5252
5386
  for (const hit of semanticHits) {
5253
- if (hit.score < input.min_semantic_score) {
5254
- const existing = seen.get(hit.id);
5255
- if (!existing) continue;
5256
- }
5387
+ if (hit.score < input.min_semantic_score && !seen.has(hit.id)) continue;
5257
5388
  const loaded = byId.get(hit.id);
5258
5389
  if (loaded) addOrUpdate(loaded, "semantic", hit.score, "semantic");
5259
5390
  }
5260
5391
  }
5261
5392
  }
5262
5393
  const ranked = [...seen.values()].sort((a, b) => {
5263
- const priorityScore = (m) => priorityRank(classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols));
5264
- const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
5265
- (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);
5394
+ 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);
5266
5395
  const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
5267
- const sa = priorityScore(a) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
5268
- const sb = priorityScore(b) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
5396
+ const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
5397
+ const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
5269
5398
  return sb - sa;
5270
5399
  });
5271
5400
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -5294,11 +5423,7 @@ async function getBriefing(input, ctx) {
5294
5423
  if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
5295
5424
  const newFm = { ...loaded.memory.frontmatter, status: "validated" };
5296
5425
  try {
5297
- await writeFile11(
5298
- loaded.filePath,
5299
- serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }),
5300
- "utf8"
5301
- );
5426
+ await writeFile11(loaded.filePath, serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
5302
5427
  m.status = "validated";
5303
5428
  m.confidence = "trusted";
5304
5429
  } catch {
@@ -5306,12 +5431,12 @@ async function getBriefing(input, ctx) {
5306
5431
  }
5307
5432
  }
5308
5433
  }
5309
- const projectContextRaw = input.include_project_context && existsSync18(ctx.paths.projectContext) ? await readFile32(ctx.paths.projectContext, "utf8") : "";
5434
+ const projectContextRaw = input.include_project_context && existsSync19(ctx.paths.projectContext) ? await readFile42(ctx.paths.projectContext, "utf8") : "";
5310
5435
  const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
5311
5436
  const setupWarnings = [];
5312
5437
  let autoContextGenerated = false;
5313
5438
  let projectContext = isTemplateContext ? "" : projectContextRaw;
5314
- if ((isTemplateContext || !existsSync18(ctx.paths.projectContext)) && input.include_project_context) {
5439
+ if ((isTemplateContext || !existsSync19(ctx.paths.projectContext)) && input.include_project_context) {
5315
5440
  const haiveConfig = await loadConfig3(ctx.paths);
5316
5441
  if (haiveConfig.autoContext) {
5317
5442
  const codeMap = await loadCodeMap5(ctx.paths);
@@ -5347,15 +5472,9 @@ async function getBriefing(input, ctx) {
5347
5472
  );
5348
5473
  }
5349
5474
  } else {
5350
- if (isTemplateContext) {
5351
- setupWarnings.push(
5352
- "project-context.md still contains the default template. Invoke the bootstrap_project MCP prompt to auto-fill it from your codebase. Until then, get_briefing returns no project context."
5353
- );
5354
- } else {
5355
- setupWarnings.push(
5356
- "No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
5357
- );
5358
- }
5475
+ setupWarnings.push(
5476
+ isTemplateContext ? "project-context.md still contains the default template. Invoke the bootstrap_project MCP prompt to auto-fill it from your codebase. Until then, get_briefing returns no project context." : "No project-context.md found. Run `haive init` then invoke the bootstrap_project MCP prompt."
5477
+ );
5359
5478
  }
5360
5479
  }
5361
5480
  const moduleContents = briefingIncludeModules ? await loadModuleContexts2(ctx, inferred) : [];
@@ -5418,10 +5537,7 @@ ${m.content}`).join("\n\n---\n\n"),
5418
5537
  const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
5419
5538
  if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
5420
5539
  }
5421
- const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({
5422
- ...m,
5423
- body: extractActionsBriefBody2(m.body)
5424
- })) : trimmedMemories;
5540
+ const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({ ...m, body: extractActionsBriefBody2(m.body) })) : trimmedMemories;
5425
5541
  const outputMemories = formattedMemories.map((m) => ({
5426
5542
  ...m,
5427
5543
  priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
@@ -5436,8 +5552,7 @@ ${m.content}`).join("\n\n---\n\n"),
5436
5552
  let symbolLocations;
5437
5553
  const symbolsToLookup = new Set(input.symbols);
5438
5554
  for (const m of outputMemories) {
5439
- const loaded = byId.get(m.id);
5440
- for (const sym of loaded?.memory.frontmatter.anchor.symbols ?? []) {
5555
+ for (const sym of byId.get(m.id)?.memory.frontmatter.anchor.symbols ?? []) {
5441
5556
  symbolsToLookup.add(sym);
5442
5557
  }
5443
5558
  }
@@ -5465,41 +5580,37 @@ ${m.content}`).join("\n\n---\n\n"),
5465
5580
  }
5466
5581
  }
5467
5582
  const actionRequired = [];
5468
- for (const m of outputMemories) {
5469
- const loaded = byId.get(m.id);
5470
- if (!loaded?.memory.frontmatter.requires_human_approval) continue;
5471
- const bodyLines = loaded.memory.body.split("\n");
5583
+ const extractActionItem = (id, body) => {
5584
+ const bodyLines = body.split("\n");
5472
5585
  const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
5473
5586
  const headingLine = bodyLines.find((l) => l.startsWith("## "));
5474
- const summary = headingLine?.replace(/^##\s*/, "").trim() ?? m.id;
5475
- actionRequired.push({
5476
- id: m.id,
5587
+ const summary = headingLine?.replace(/^##\s*/, "").trim() ?? id;
5588
+ return {
5589
+ id,
5477
5590
  summary,
5478
- developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${m.id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
5479
- });
5591
+ developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
5592
+ };
5593
+ };
5594
+ for (const m of outputMemories) {
5595
+ const loaded = byId.get(m.id);
5596
+ if (loaded?.memory.frontmatter.requires_human_approval) {
5597
+ actionRequired.push(extractActionItem(m.id, loaded.memory.body));
5598
+ }
5480
5599
  }
5481
- if (existsSync18(ctx.paths.memoriesDir)) {
5600
+ if (existsSync19(ctx.paths.memoriesDir)) {
5482
5601
  const allMems = await loadMemoriesFromDir13(ctx.paths.memoriesDir);
5483
5602
  for (const { memory: memory2 } of allMems) {
5484
5603
  const fm = memory2.frontmatter;
5485
5604
  if (!fm.requires_human_approval) continue;
5486
5605
  if (fm.status === "rejected" || fm.status === "deprecated") continue;
5487
5606
  if (actionRequired.some((a) => a.id === fm.id)) continue;
5488
- const bodyLines = memory2.body.split("\n");
5489
- const quoteBlock = bodyLines.filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join(" ").replace(/^\*«\s*/, "").replace(/\s*»\*$/, "").trim();
5490
- const headingLine = bodyLines.find((l) => l.startsWith("## "));
5491
- const summary = headingLine?.replace(/^##\s*/, "").trim() ?? fm.id;
5492
- actionRequired.push({
5493
- id: fm.id,
5494
- summary,
5495
- developer_message: quoteBlock || `Une modification externe potentiellement incompatible a \xE9t\xE9 d\xE9tect\xE9e (${fm.id}). Veux-tu que j'analyse l'impact et que je propose des mises \xE0 jour ?`
5496
- });
5607
+ actionRequired.push(extractActionItem(fm.id, memory2.body));
5497
5608
  }
5498
5609
  }
5499
5610
  const pendingDistillFile = pendingDistillPath(ctx);
5500
- if (existsSync18(pendingDistillFile)) {
5611
+ if (existsSync19(pendingDistillFile)) {
5501
5612
  try {
5502
- const raw = await readFile32(pendingDistillFile, "utf8");
5613
+ const raw = await readFile42(pendingDistillFile, "utf8");
5503
5614
  const pd = JSON.parse(raw);
5504
5615
  const ageMs = Date.now() - new Date(pd.session_end).getTime();
5505
5616
  const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
@@ -5526,7 +5637,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5526
5637
  }
5527
5638
  }
5528
5639
  const memoriesEmpty = outputMemories.length === 0;
5529
- const hasMemoriesDir = existsSync18(ctx.paths.memoriesDir);
5640
+ const hasMemoriesDir = existsSync19(ctx.paths.memoriesDir);
5530
5641
  const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
5531
5642
  const hasUnguessableSignal = outputMemories.some(
5532
5643
  (m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore2(m.body) >= GUESSABLE_THRESHOLD
@@ -5573,7 +5684,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5573
5684
  "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."
5574
5685
  );
5575
5686
  }
5576
- if (existsSync18(ctx.paths.haiveDir)) {
5687
+ if (existsSync19(ctx.paths.haiveDir)) {
5577
5688
  await writeBriefingMarker2(ctx.paths, {
5578
5689
  sessionId: process.env.HAIVE_SESSION_ID,
5579
5690
  ...input.task ? { task: input.task } : {},
@@ -5621,144 +5732,6 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5621
5732
  }
5622
5733
  };
5623
5734
  }
5624
- function compactSummary(body) {
5625
- for (const line of body.split("\n")) {
5626
- const trimmed = line.replace(/^#+\s*/, "").trim();
5627
- if (trimmed.length > 0) return trimmed.slice(0, 120);
5628
- }
5629
- return body.slice(0, 120);
5630
- }
5631
- function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
5632
- const fm = loaded?.memory.frontmatter;
5633
- const directAnchor = Boolean(
5634
- fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
5635
- );
5636
- const directSymbol = Boolean(
5637
- fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
5638
- (sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
5639
- )
5640
- );
5641
- const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
5642
- const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
5643
- if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
5644
- return "must_read";
5645
- }
5646
- if (isStackPackSeed2(fm)) {
5647
- return "background";
5648
- }
5649
- if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
5650
- return "useful";
5651
- }
5652
- return "background";
5653
- }
5654
- function priorityRank(priority) {
5655
- return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
5656
- }
5657
- function classifyBriefingQuality(memories, context) {
5658
- const mustRead = memories.filter((m) => m.priority === "must_read").length;
5659
- const useful = memories.filter((m) => m.priority === "useful").length;
5660
- const background = memories.filter((m) => m.priority === "background").length;
5661
- const weakSemantic = memories.filter(
5662
- (m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
5663
- ).length;
5664
- const reasons = [];
5665
- if (memories.length === 0) reasons.push("no memories matched the task or files");
5666
- if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
5667
- if (!context.hasLastSession) reasons.push("no previous session recap");
5668
- if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
5669
- if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
5670
- if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
5671
- if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
5672
- if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
5673
- if (memories.length === 0 || mustRead === 0 && useful === 0) {
5674
- return { level: "thin", reasons };
5675
- }
5676
- if (background > useful + mustRead && background > 2) {
5677
- return { level: "noisy", reasons };
5678
- }
5679
- return { level: "strong", reasons };
5680
- }
5681
- function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
5682
- const why = [];
5683
- const fm = loaded?.memory.frontmatter;
5684
- if (memory2.reasons.includes("anchor") && fm) {
5685
- const matching = fm.anchor.paths.filter(
5686
- (p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
5687
- );
5688
- if (matching.length > 0) {
5689
- const exact = matching.filter(
5690
- (p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
5691
- );
5692
- const glob = matching.filter((p) => isGlobPath(p));
5693
- if (exact.length > 0) {
5694
- why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
5695
- }
5696
- if (glob.length > 0) {
5697
- why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
5698
- }
5699
- if (exact.length === 0 && glob.length === 0) {
5700
- why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
5701
- }
5702
- } else if (fm.anchor.paths.length > 0) {
5703
- why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
5704
- }
5705
- if (fm.anchor.symbols.length > 0) {
5706
- why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
5707
- }
5708
- }
5709
- if (memory2.reasons.includes("symbol") && fm) {
5710
- why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
5711
- }
5712
- if (memory2.reasons.includes("module")) {
5713
- const moduleHints = [
5714
- ...memory2.module ? [memory2.module] : [],
5715
- ...memory2.tags.filter((tag) => inferredModules.includes(tag))
5716
- ];
5717
- const shown = moduleHints.length > 0 ? [...new Set(moduleHints)].join(", ") : inferredModules.join(", ");
5718
- why.push(shown ? `Matched inferred module/tag: ${shown}` : "Matched inferred module context.");
5719
- }
5720
- if (memory2.reasons.includes("domain")) {
5721
- why.push("Matched inferred domain from the target file paths.");
5722
- }
5723
- if (memory2.reasons.includes("semantic")) {
5724
- const score = memory2.semantic_score !== void 0 ? ` score=${Math.round(memory2.semantic_score * 100) / 100}` : "";
5725
- why.push(`${memory2.match_quality === "exact" ? "Literal task match" : "Semantic/task relevance"}${score}.`);
5726
- }
5727
- why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
5728
- if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
5729
- if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
5730
- if (memory2.status === "proposed" || memory2.status === "draft") {
5731
- why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
5732
- }
5733
- return why;
5734
- }
5735
- async function trySemanticHits(ctx, task, limit) {
5736
- let mod;
5737
- try {
5738
- mod = await import("@hiveai/embeddings");
5739
- } catch {
5740
- return null;
5741
- }
5742
- const result = await mod.semanticSearch(ctx.paths, task, { limit });
5743
- if (!result) return null;
5744
- return result.hits.map((h) => ({ id: h.id, score: h.score }));
5745
- }
5746
- async function loadModuleContexts2(ctx, modules) {
5747
- if (modules.length === 0) return [];
5748
- if (!existsSync18(ctx.paths.modulesContextDir)) return [];
5749
- const available = new Set(
5750
- (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
5751
- );
5752
- const out = [];
5753
- for (const m of modules) {
5754
- if (!available.has(m)) continue;
5755
- const file = path92.join(ctx.paths.modulesContextDir, m, "context.md");
5756
- if (existsSync18(file)) {
5757
- out.push({ name: m, content: await readFile32(file, "utf8") });
5758
- }
5759
- }
5760
- return out;
5761
- }
5762
5735
  var CodeMapInputSchema = {
5763
5736
  file: z18.string().optional().describe("Filter to files whose path contains this substring"),
5764
5737
  symbol: z18.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
@@ -5842,7 +5815,7 @@ var MemDiffInputSchema = {
5842
5815
  id_b: z19.string().min(1).describe("Second memory id")
5843
5816
  };
5844
5817
  async function memDiff(input, ctx) {
5845
- if (!existsSync19(ctx.paths.memoriesDir)) {
5818
+ if (!existsSync20(ctx.paths.memoriesDir)) {
5846
5819
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
5847
5820
  }
5848
5821
  const all = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
@@ -5883,7 +5856,7 @@ var GetRecapInputSchema = {
5883
5856
  )
5884
5857
  };
5885
5858
  async function getRecap(input, ctx) {
5886
- if (!existsSync20(ctx.paths.memoriesDir)) {
5859
+ if (!existsSync21(ctx.paths.memoriesDir)) {
5887
5860
  return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
5888
5861
  }
5889
5862
  const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
@@ -5982,7 +5955,7 @@ var WhyThisFileInputSchema = {
5982
5955
  memory_limit: z23.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
5983
5956
  };
5984
5957
  async function whyThisFile(input, ctx) {
5985
- const fileExists = existsSync21(path102.join(ctx.paths.root, input.path));
5958
+ const fileExists = existsSync222(path102.join(ctx.paths.root, input.path));
5986
5959
  const [commits, memories, codeMap] = await Promise.all([
5987
5960
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
5988
5961
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -6023,7 +5996,7 @@ async function whyThisFile(input, ctx) {
6023
5996
  };
6024
5997
  }
6025
5998
  async function collectAnchoredMemories(ctx, filePath, limit) {
6026
- if (!existsSync21(ctx.paths.memoriesDir)) return [];
5999
+ if (!existsSync222(ctx.paths.memoriesDir)) return [];
6027
6000
  const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
6028
6001
  const usage = await loadUsageIndex8(ctx.paths);
6029
6002
  const out = [];
@@ -6155,7 +6128,7 @@ async function antiPatternsCheck(input, ctx) {
6155
6128
  notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
6156
6129
  };
6157
6130
  }
6158
- if (!existsSync222(ctx.paths.memoriesDir)) {
6131
+ if (!existsSync23(ctx.paths.memoriesDir)) {
6159
6132
  return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
6160
6133
  }
6161
6134
  const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
@@ -6190,6 +6163,7 @@ async function antiPatternsCheck(input, ctx) {
6190
6163
  reasons: [reason],
6191
6164
  tags: fm.tags ?? [],
6192
6165
  anchor_paths: fm.anchor?.paths ?? [],
6166
+ ...fm.sensor != null ? { has_sensor: true } : {},
6193
6167
  ...score !== void 0 ? { semantic_score: score } : {}
6194
6168
  });
6195
6169
  };
@@ -6302,7 +6276,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
6302
6276
  "error"
6303
6277
  ]);
6304
6278
  async function memDistill(input, ctx) {
6305
- if (!existsSync23(ctx.paths.memoriesDir)) {
6279
+ if (!existsSync24(ctx.paths.memoriesDir)) {
6306
6280
  return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
6307
6281
  }
6308
6282
  const cutoff = Date.now() - input.since_days * MS_PER_DAY;
@@ -6413,7 +6387,7 @@ var WhyThisDecisionInputSchema = {
6413
6387
  git_log_limit: z26.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
6414
6388
  };
6415
6389
  async function whyThisDecision(input, ctx) {
6416
- if (!existsSync24(ctx.paths.memoriesDir)) {
6390
+ if (!existsSync25(ctx.paths.memoriesDir)) {
6417
6391
  return {
6418
6392
  found: false,
6419
6393
  related: [],
@@ -6551,7 +6525,7 @@ var MemConflictsInputSchema = {
6551
6525
  var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
6552
6526
  var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
6553
6527
  async function memConflicts(input, ctx) {
6554
- if (!existsSync25(ctx.paths.memoriesDir)) {
6528
+ if (!existsSync26(ctx.paths.memoriesDir)) {
6555
6529
  return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
6556
6530
  }
6557
6531
  const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
@@ -6772,6 +6746,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
6772
6746
  };
6773
6747
  }
6774
6748
  if (isBlockingWarning(warning)) {
6749
+ if (warning.has_sensor && !warning.reasons.includes("sensor")) {
6750
+ return {
6751
+ ...warning,
6752
+ level: "review",
6753
+ rationale: "memory has a sensor that did not fire \u2014 sensor is the authoritative check; strong semantic match alone is insufficient to block",
6754
+ affected_files: affectedFiles,
6755
+ repair_command: repairCommand
6756
+ };
6757
+ }
6775
6758
  return {
6776
6759
  ...warning,
6777
6760
  level: "blocking",
@@ -6784,6 +6767,15 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
6784
6767
  const semanticScore = warning.semantic_score ?? 0;
6785
6768
  const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
6786
6769
  if (anchoredBlocks && highConfidence && warning.reasons.includes("anchor") && (warning.reasons.includes("literal") || hasSemantic && semanticScore >= 0.45)) {
6770
+ if (warning.has_sensor && !warning.reasons.includes("sensor")) {
6771
+ return {
6772
+ ...warning,
6773
+ level: "review",
6774
+ rationale: "memory has a sensor that did not fire \u2014 literal match alone is insufficient to block; sensor is the authoritative check",
6775
+ affected_files: affectedFiles,
6776
+ repair_command: repairCommand
6777
+ };
6778
+ }
6787
6779
  return {
6788
6780
  ...warning,
6789
6781
  level: "blocking",
@@ -6938,7 +6930,7 @@ var PatternDetectInputSchema = {
6938
6930
  scope: z29.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
6939
6931
  };
6940
6932
  async function patternDetect(input, ctx) {
6941
- if (!existsSync26(ctx.paths.haiveDir)) {
6933
+ if (!existsSync27(ctx.paths.haiveDir)) {
6942
6934
  return {
6943
6935
  scanned_events: 0,
6944
6936
  matches: [],
@@ -7067,7 +7059,7 @@ async function patternDetect(input, ctx) {
7067
7059
  fm.id,
7068
7060
  void 0
7069
7061
  );
7070
- if (existsSync26(file)) continue;
7062
+ if (existsSync27(file)) continue;
7071
7063
  await mkdir72(path112.dirname(file), { recursive: true });
7072
7064
  await writeFile12(
7073
7065
  file,
@@ -7122,7 +7114,7 @@ var MemConflictCandidatesInputSchema = {
7122
7114
  )
7123
7115
  };
7124
7116
  async function memConflictCandidates(input, ctx) {
7125
- if (!existsSync27(ctx.paths.memoriesDir)) {
7117
+ if (!existsSync28(ctx.paths.memoriesDir)) {
7126
7118
  return {
7127
7119
  pairs: [],
7128
7120
  topic_status_pairs: [],
@@ -7170,7 +7162,7 @@ var MemTimelineInputSchema = {
7170
7162
  limit: z33.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
7171
7163
  };
7172
7164
  async function memTimeline(input, ctx) {
7173
- if (!existsSync28(ctx.paths.memoriesDir)) {
7165
+ if (!existsSync29(ctx.paths.memoriesDir)) {
7174
7166
  return { entries: [], total: 0, notice: "No .ai/memories directory." };
7175
7167
  }
7176
7168
  const all = await loadMemoriesFromDir222(ctx.paths.memoriesDir);
@@ -7442,7 +7434,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7442
7434
  };
7443
7435
  }
7444
7436
  var SERVER_NAME = "haive";
7445
- var SERVER_VERSION = "0.10.5";
7437
+ var SERVER_VERSION = "0.10.8";
7446
7438
  function jsonResult(data) {
7447
7439
  return {
7448
7440
  content: [
@@ -7546,11 +7538,16 @@ function createHaiveServer(options = {}) {
7546
7538
  return await handler(input);
7547
7539
  }
7548
7540
  if (requireBriefingFirst && MUTATING_TOOLS.has(name) && !briefingLoaded) {
7549
- return jsonResult({
7550
- error: "haive_briefing_required",
7551
- message: "This hAIve project requires get_briefing or mem_relevant_to before state-changing hAIve tools. Call get_briefing({ task: '...' }) first.",
7552
- tool: name
7553
- });
7541
+ const hasDiskMarker = await hasRecentBriefingMarker(context.paths).catch(() => false);
7542
+ if (hasDiskMarker) {
7543
+ briefingLoaded = true;
7544
+ } else {
7545
+ return jsonResult({
7546
+ error: "haive_briefing_required",
7547
+ message: "This hAIve project requires get_briefing or mem_relevant_to before state-changing hAIve tools. Call get_briefing({ task: '...' }) first.",
7548
+ tool: name
7549
+ });
7550
+ }
7554
7551
  }
7555
7552
  return await handler(input);
7556
7553
  }
@@ -8412,7 +8409,7 @@ function registerMcp(program2) {
8412
8409
  // src/commands/sync.ts
8413
8410
  import { spawnSync as spawnSync4 } from "child_process";
8414
8411
  import { readFile as readFile9, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
8415
- import { existsSync as existsSync29 } from "fs";
8412
+ import { existsSync as existsSync30 } from "fs";
8416
8413
  import path15 from "path";
8417
8414
  import "commander";
8418
8415
  import {
@@ -8449,7 +8446,7 @@ function registerSync(program2) {
8449
8446
  ).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) => {
8450
8447
  const root = findProjectRoot12(opts.dir);
8451
8448
  const paths = resolveHaivePaths9(root);
8452
- if (!existsSync29(paths.memoriesDir)) {
8449
+ if (!existsSync30(paths.memoriesDir)) {
8453
8450
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8454
8451
  process.exitCode = 1;
8455
8452
  return;
@@ -8873,7 +8870,7 @@ function bridgeSummaryLine(body) {
8873
8870
  return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
8874
8871
  }
8875
8872
  async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8876
- if (!existsSync29(memoriesDir)) return;
8873
+ if (!existsSync30(memoriesDir)) return;
8877
8874
  const all = await loadMemoriesFromDir23(memoriesDir);
8878
8875
  const top = all.filter(({ memory: memory2 }) => {
8879
8876
  const s = memory2.frontmatter.status;
@@ -8899,7 +8896,7 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8899
8896
  ` + block + `
8900
8897
 
8901
8898
  ${BRIDGE_END}`;
8902
- const fileExists = existsSync29(bridgeFile);
8899
+ const fileExists = existsSync30(bridgeFile);
8903
8900
  let existing = fileExists ? await readFile9(bridgeFile, "utf8") : "";
8904
8901
  existing = existing.replace(/\r\n/g, "\n");
8905
8902
  const startIdx = existing.indexOf(BRIDGE_START);
@@ -8950,7 +8947,7 @@ function collectSinceChanges(root, ref) {
8950
8947
  // src/commands/memory-add.ts
8951
8948
  import { createHash as createHash2 } from "crypto";
8952
8949
  import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
8953
- import { existsSync as existsSync30 } from "fs";
8950
+ import { existsSync as existsSync31 } from "fs";
8954
8951
  import path16 from "path";
8955
8952
  import "commander";
8956
8953
  import {
@@ -8992,7 +8989,7 @@ function registerMemoryAdd(memory2) {
8992
8989
  ).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) => {
8993
8990
  const root = findProjectRoot13(opts.dir);
8994
8991
  const paths = resolveHaivePaths10(root);
8995
- if (!existsSync30(paths.haiveDir)) {
8992
+ if (!existsSync31(paths.haiveDir)) {
8996
8993
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
8997
8994
  process.exitCode = 1;
8998
8995
  return;
@@ -9004,7 +9001,7 @@ function registerMemoryAdd(memory2) {
9004
9001
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
9005
9002
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
9006
9003
  if (anchorPaths.length > 0) {
9007
- const missing = anchorPaths.filter((p) => !existsSync30(path16.resolve(root, p)));
9004
+ const missing = anchorPaths.filter((p) => !existsSync31(path16.resolve(root, p)));
9008
9005
  if (missing.length > 0) {
9009
9006
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
9010
9007
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -9017,7 +9014,7 @@ function registerMemoryAdd(memory2) {
9017
9014
  const slug = slugify(opts.slug ?? opts.title ?? opts.topic ?? opts.body ?? `${opts.type}-memory`);
9018
9015
  let body;
9019
9016
  if (opts.bodyFile !== void 0) {
9020
- if (!existsSync30(opts.bodyFile)) {
9017
+ if (!existsSync31(opts.bodyFile)) {
9021
9018
  ui.error(`--body-file not found: ${opts.bodyFile}`);
9022
9019
  process.exitCode = 1;
9023
9020
  return;
@@ -9033,7 +9030,7 @@ TODO \u2014 write the memory body.
9033
9030
  `;
9034
9031
  }
9035
9032
  const scope = opts.scope ?? config.defaultScope ?? "personal";
9036
- if (existsSync30(paths.memoriesDir)) {
9033
+ if (existsSync31(paths.memoriesDir)) {
9037
9034
  const incomingHash = createHash2("sha256").update(body.trim()).digest("hex").slice(0, 12);
9038
9035
  const allForHash = await loadMemoriesFromDir24(paths.memoriesDir);
9039
9036
  const hashDup = allForHash.find(
@@ -9046,7 +9043,7 @@ TODO \u2014 write the memory body.
9046
9043
  return;
9047
9044
  }
9048
9045
  }
9049
- if (opts.topic && existsSync30(paths.memoriesDir)) {
9046
+ if (opts.topic && existsSync31(paths.memoriesDir)) {
9050
9047
  const existing = await loadMemoriesFromDir24(paths.memoriesDir);
9051
9048
  const topicMatch = existing.find(
9052
9049
  ({ memory: memory3 }) => memory3.frontmatter.topic === opts.topic && memory3.frontmatter.scope === scope && (!opts.module || memory3.frontmatter.module === opts.module)
@@ -9091,12 +9088,12 @@ TODO \u2014 write the memory body.
9091
9088
  });
9092
9089
  const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9093
9090
  await mkdir11(path16.dirname(file), { recursive: true });
9094
- if (existsSync30(file)) {
9091
+ if (existsSync31(file)) {
9095
9092
  ui.error(`Memory already exists at ${file}`);
9096
9093
  process.exitCode = 1;
9097
9094
  return;
9098
9095
  }
9099
- if (existsSync30(paths.memoriesDir)) {
9096
+ if (existsSync31(paths.memoriesDir)) {
9100
9097
  const existing = await loadMemoriesFromDir24(paths.memoriesDir);
9101
9098
  const slugTokens = slug.toLowerCase().split(/[-_\s]+/).filter(Boolean);
9102
9099
  const similar = existing.filter(({ memory: memory3 }) => {
@@ -9188,7 +9185,7 @@ function slugify(value) {
9188
9185
  }
9189
9186
 
9190
9187
  // src/commands/memory-list.ts
9191
- import { existsSync as existsSync31 } from "fs";
9188
+ import { existsSync as existsSync33 } from "fs";
9192
9189
  import path17 from "path";
9193
9190
  import "commander";
9194
9191
  import { findProjectRoot as findProjectRoot14, resolveHaivePaths as resolveHaivePaths11 } from "@hiveai/core";
@@ -9205,7 +9202,7 @@ function registerMemoryList(memory2) {
9205
9202
  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) => {
9206
9203
  const root = findProjectRoot14(opts.dir);
9207
9204
  const paths = resolveHaivePaths11(root);
9208
- if (!existsSync31(paths.memoriesDir)) {
9205
+ if (!existsSync33(paths.memoriesDir)) {
9209
9206
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9210
9207
  process.exitCode = 1;
9211
9208
  return;
@@ -9280,7 +9277,7 @@ function matchesFilters(loaded, opts) {
9280
9277
 
9281
9278
  // src/commands/memory-promote.ts
9282
9279
  import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
9283
- import { existsSync as existsSync33 } from "fs";
9280
+ import { existsSync as existsSync34 } from "fs";
9284
9281
  import path18 from "path";
9285
9282
  import "commander";
9286
9283
  import {
@@ -9293,7 +9290,7 @@ function registerMemoryPromote(memory2) {
9293
9290
  memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
9294
9291
  const root = findProjectRoot15(opts.dir);
9295
9292
  const paths = resolveHaivePaths12(root);
9296
- if (!existsSync33(paths.memoriesDir)) {
9293
+ if (!existsSync34(paths.memoriesDir)) {
9297
9294
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9298
9295
  process.exitCode = 1;
9299
9296
  return;
@@ -9338,7 +9335,7 @@ function registerMemoryPromote(memory2) {
9338
9335
  }
9339
9336
 
9340
9337
  // src/commands/memory-approve.ts
9341
- import { existsSync as existsSync34 } from "fs";
9338
+ import { existsSync as existsSync35 } from "fs";
9342
9339
  import { writeFile as writeFile16 } from "fs/promises";
9343
9340
  import path19 from "path";
9344
9341
  import "commander";
@@ -9351,7 +9348,7 @@ function registerMemoryApprove(memory2) {
9351
9348
  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) => {
9352
9349
  const root = findProjectRoot16(opts.dir);
9353
9350
  const paths = resolveHaivePaths13(root);
9354
- if (!existsSync34(paths.memoriesDir)) {
9351
+ if (!existsSync35(paths.memoriesDir)) {
9355
9352
  ui.error(`No .ai/memories at ${root}.`);
9356
9353
  process.exitCode = 1;
9357
9354
  return;
@@ -9410,7 +9407,7 @@ function registerMemoryApprove(memory2) {
9410
9407
 
9411
9408
  // src/commands/memory-update.ts
9412
9409
  import { readFile as readFile11, writeFile as writeFile17 } from "fs/promises";
9413
- import { existsSync as existsSync35 } from "fs";
9410
+ import { existsSync as existsSync36 } from "fs";
9414
9411
  import path20 from "path";
9415
9412
  import "commander";
9416
9413
  import {
@@ -9422,7 +9419,7 @@ function registerMemoryUpdate(memory2) {
9422
9419
  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) => {
9423
9420
  const root = findProjectRoot17(opts.dir);
9424
9421
  const paths = resolveHaivePaths14(root);
9425
- if (!existsSync35(paths.memoriesDir)) {
9422
+ if (!existsSync36(paths.memoriesDir)) {
9426
9423
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9427
9424
  process.exitCode = 1;
9428
9425
  return;
@@ -9464,7 +9461,7 @@ function registerMemoryUpdate(memory2) {
9464
9461
  if (opts.author !== void 0) updated.push("author");
9465
9462
  let newBody;
9466
9463
  if (opts.bodyFile !== void 0) {
9467
- if (!existsSync35(opts.bodyFile)) {
9464
+ if (!existsSync36(opts.bodyFile)) {
9468
9465
  ui.error(`--body-file not found: ${opts.bodyFile}`);
9469
9466
  process.exitCode = 1;
9470
9467
  return;
@@ -9510,7 +9507,7 @@ function parseCsv3(value) {
9510
9507
 
9511
9508
  // src/commands/memory-auto-promote.ts
9512
9509
  import { writeFile as writeFile18 } from "fs/promises";
9513
- import { existsSync as existsSync36 } from "fs";
9510
+ import { existsSync as existsSync37 } from "fs";
9514
9511
  import path21 from "path";
9515
9512
  import "commander";
9516
9513
  import {
@@ -9530,7 +9527,7 @@ function registerMemoryAutoPromote(memory2) {
9530
9527
  ).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
9531
9528
  const root = findProjectRoot18(opts.dir);
9532
9529
  const paths = resolveHaivePaths15(root);
9533
- if (!existsSync36(paths.memoriesDir)) {
9530
+ if (!existsSync37(paths.memoriesDir)) {
9534
9531
  ui.error(`No .ai/memories at ${root}.`);
9535
9532
  process.exitCode = 1;
9536
9533
  return;
@@ -9573,7 +9570,7 @@ function registerMemoryAutoPromote(memory2) {
9573
9570
 
9574
9571
  // src/commands/memory-edit.ts
9575
9572
  import { spawn as spawn3 } from "child_process";
9576
- import { existsSync as existsSync37 } from "fs";
9573
+ import { existsSync as existsSync38 } from "fs";
9577
9574
  import { readFile as readFile12 } from "fs/promises";
9578
9575
  import path23 from "path";
9579
9576
  import "commander";
@@ -9586,7 +9583,7 @@ function registerMemoryEdit(memory2) {
9586
9583
  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) => {
9587
9584
  const root = findProjectRoot19(opts.dir);
9588
9585
  const paths = resolveHaivePaths16(root);
9589
- if (!existsSync37(paths.memoriesDir)) {
9586
+ if (!existsSync38(paths.memoriesDir)) {
9590
9587
  ui.error(`No .ai/memories at ${root}.`);
9591
9588
  process.exitCode = 1;
9592
9589
  return;
@@ -9626,7 +9623,7 @@ function runEditor(editor, file) {
9626
9623
  }
9627
9624
 
9628
9625
  // src/commands/memory-for-files.ts
9629
- import { existsSync as existsSync38 } from "fs";
9626
+ import { existsSync as existsSync39 } from "fs";
9630
9627
  import path24 from "path";
9631
9628
  import "commander";
9632
9629
  import {
@@ -9642,7 +9639,7 @@ function registerMemoryForFiles(memory2) {
9642
9639
  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) => {
9643
9640
  const root = findProjectRoot20(opts.dir);
9644
9641
  const paths = resolveHaivePaths17(root);
9645
- if (!existsSync38(paths.memoriesDir)) {
9642
+ if (!existsSync39(paths.memoriesDir)) {
9646
9643
  ui.error(`No .ai/memories at ${root}.`);
9647
9644
  process.exitCode = 1;
9648
9645
  return;
@@ -9754,7 +9751,7 @@ function printGroup(root, label, loaded, usage) {
9754
9751
  }
9755
9752
 
9756
9753
  // src/commands/memory-hot.ts
9757
- import { existsSync as existsSync39 } from "fs";
9754
+ import { existsSync as existsSync40 } from "fs";
9758
9755
  import path25 from "path";
9759
9756
  import "commander";
9760
9757
  import {
@@ -9769,7 +9766,7 @@ function registerMemoryHot(memory2) {
9769
9766
  ).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) => {
9770
9767
  const root = findProjectRoot21(opts.dir);
9771
9768
  const paths = resolveHaivePaths18(root);
9772
- if (!existsSync39(paths.memoriesDir)) {
9769
+ if (!existsSync40(paths.memoriesDir)) {
9773
9770
  ui.error(`No .ai/memories at ${root}.`);
9774
9771
  process.exitCode = 1;
9775
9772
  return;
@@ -9808,7 +9805,7 @@ function registerMemoryHot(memory2) {
9808
9805
 
9809
9806
  // src/commands/memory-tried.ts
9810
9807
  import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
9811
- import { existsSync as existsSync40 } from "fs";
9808
+ import { existsSync as existsSync41 } from "fs";
9812
9809
  import path26 from "path";
9813
9810
  import "commander";
9814
9811
  import {
@@ -9838,7 +9835,7 @@ function registerMemoryTried(memory2) {
9838
9835
  ).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) => {
9839
9836
  const root = findProjectRoot22(opts.dir);
9840
9837
  const paths = resolveHaivePaths19(root);
9841
- if (!existsSync40(paths.haiveDir)) {
9838
+ if (!existsSync41(paths.haiveDir)) {
9842
9839
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9843
9840
  process.exitCode = 1;
9844
9841
  return;
@@ -9866,7 +9863,7 @@ function registerMemoryTried(memory2) {
9866
9863
  }
9867
9864
  const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9868
9865
  await mkdir13(path26.dirname(file), { recursive: true });
9869
- if (existsSync40(file)) {
9866
+ if (existsSync41(file)) {
9870
9867
  ui.error(`Memory already exists at ${file}`);
9871
9868
  process.exitCode = 1;
9872
9869
  return;
@@ -9884,7 +9881,7 @@ function parseCsv4(value) {
9884
9881
 
9885
9882
  // src/commands/memory-seed.ts
9886
9883
  import { readFile as readFile13 } from "fs/promises";
9887
- import { existsSync as existsSync41 } from "fs";
9884
+ import { existsSync as existsSync43 } from "fs";
9888
9885
  import path27 from "path";
9889
9886
  import "commander";
9890
9887
  import {
@@ -9924,7 +9921,7 @@ function registerMemorySeed(memory2) {
9924
9921
  }
9925
9922
  return;
9926
9923
  }
9927
- if (!existsSync41(paths.haiveDir)) {
9924
+ if (!existsSync43(paths.haiveDir)) {
9928
9925
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9929
9926
  process.exitCode = 1;
9930
9927
  return;
@@ -9980,7 +9977,7 @@ function registerMemorySeed(memory2) {
9980
9977
  }
9981
9978
 
9982
9979
  // src/commands/memory-pending.ts
9983
- import { existsSync as existsSync43 } from "fs";
9980
+ import { existsSync as existsSync44 } from "fs";
9984
9981
  import path28 from "path";
9985
9982
  import "commander";
9986
9983
  import {
@@ -9993,7 +9990,7 @@ function registerMemoryPending(memory2) {
9993
9990
  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) => {
9994
9991
  const root = findProjectRoot24(opts.dir);
9995
9992
  const paths = resolveHaivePaths21(root);
9996
- if (!existsSync43(paths.memoriesDir)) {
9993
+ if (!existsSync44(paths.memoriesDir)) {
9997
9994
  ui.error(`No .ai/memories at ${root}.`);
9998
9995
  process.exitCode = 1;
9999
9996
  return;
@@ -10051,7 +10048,7 @@ function registerMemoryPending(memory2) {
10051
10048
  }
10052
10049
 
10053
10050
  // src/commands/memory-query.ts
10054
- import { existsSync as existsSync44 } from "fs";
10051
+ import { existsSync as existsSync45 } from "fs";
10055
10052
  import path29 from "path";
10056
10053
  import "commander";
10057
10054
  import {
@@ -10068,7 +10065,7 @@ function registerMemoryQuery(memory2) {
10068
10065
  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) => {
10069
10066
  const root = findProjectRoot25(opts.dir);
10070
10067
  const paths = resolveHaivePaths22(root);
10071
- if (!existsSync44(paths.memoriesDir)) {
10068
+ if (!existsSync45(paths.memoriesDir)) {
10072
10069
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
10073
10070
  process.exitCode = 1;
10074
10071
  return;
@@ -10127,7 +10124,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
10127
10124
 
10128
10125
  // src/commands/memory-reject.ts
10129
10126
  import { writeFile as writeFile20 } from "fs/promises";
10130
- import { existsSync as existsSync45 } from "fs";
10127
+ import { existsSync as existsSync46 } from "fs";
10131
10128
  import "commander";
10132
10129
  import {
10133
10130
  findProjectRoot as findProjectRoot26,
@@ -10141,7 +10138,7 @@ function registerMemoryReject(memory2) {
10141
10138
  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) => {
10142
10139
  const root = findProjectRoot26(opts.dir);
10143
10140
  const paths = resolveHaivePaths23(root);
10144
- if (!existsSync45(paths.memoriesDir)) {
10141
+ if (!existsSync46(paths.memoriesDir)) {
10145
10142
  ui.error(`No .ai/memories at ${root}.`);
10146
10143
  process.exitCode = 1;
10147
10144
  return;
@@ -10177,7 +10174,7 @@ function registerMemoryReject(memory2) {
10177
10174
  }
10178
10175
 
10179
10176
  // src/commands/memory-rm.ts
10180
- import { existsSync as existsSync46 } from "fs";
10177
+ import { existsSync as existsSync47 } from "fs";
10181
10178
  import { unlink as unlink3 } from "fs/promises";
10182
10179
  import path30 from "path";
10183
10180
  import { createInterface as createInterface2 } from "readline/promises";
@@ -10192,7 +10189,7 @@ function registerMemoryRm(memory2) {
10192
10189
  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) => {
10193
10190
  const root = findProjectRoot27(opts.dir);
10194
10191
  const paths = resolveHaivePaths24(root);
10195
- if (!existsSync46(paths.memoriesDir)) {
10192
+ if (!existsSync47(paths.memoriesDir)) {
10196
10193
  ui.error(`No .ai/memories at ${root}.`);
10197
10194
  process.exitCode = 1;
10198
10195
  return;
@@ -10228,7 +10225,7 @@ function registerMemoryRm(memory2) {
10228
10225
  }
10229
10226
 
10230
10227
  // src/commands/memory-show.ts
10231
- import { existsSync as existsSync47 } from "fs";
10228
+ import { existsSync as existsSync48 } from "fs";
10232
10229
  import { readFile as readFile14 } from "fs/promises";
10233
10230
  import path31 from "path";
10234
10231
  import "commander";
@@ -10243,7 +10240,7 @@ function registerMemoryShow(memory2) {
10243
10240
  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) => {
10244
10241
  const root = findProjectRoot28(opts.dir);
10245
10242
  const paths = resolveHaivePaths25(root);
10246
- if (!existsSync47(paths.memoriesDir)) {
10243
+ if (!existsSync48(paths.memoriesDir)) {
10247
10244
  ui.error(`No .ai/memories at ${root}.`);
10248
10245
  process.exitCode = 1;
10249
10246
  return;
@@ -10287,7 +10284,7 @@ function registerMemoryShow(memory2) {
10287
10284
  }
10288
10285
 
10289
10286
  // src/commands/memory-stats.ts
10290
- import { existsSync as existsSync48 } from "fs";
10287
+ import { existsSync as existsSync49 } from "fs";
10291
10288
  import path33 from "path";
10292
10289
  import "commander";
10293
10290
  import {
@@ -10301,7 +10298,7 @@ function registerMemoryStats(memory2) {
10301
10298
  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) => {
10302
10299
  const root = findProjectRoot29(opts.dir);
10303
10300
  const paths = resolveHaivePaths26(root);
10304
- if (!existsSync48(paths.memoriesDir)) {
10301
+ if (!existsSync49(paths.memoriesDir)) {
10305
10302
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10306
10303
  process.exitCode = 1;
10307
10304
  return;
@@ -10333,7 +10330,7 @@ function registerMemoryStats(memory2) {
10333
10330
 
10334
10331
  // src/commands/memory-verify.ts
10335
10332
  import { writeFile as writeFile21 } from "fs/promises";
10336
- import { existsSync as existsSync49 } from "fs";
10333
+ import { existsSync as existsSync50 } from "fs";
10337
10334
  import path34 from "path";
10338
10335
  import "commander";
10339
10336
  import {
@@ -10348,7 +10345,7 @@ function registerMemoryVerify(memory2) {
10348
10345
  ).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) => {
10349
10346
  const root = findProjectRoot30(opts.dir);
10350
10347
  const paths = resolveHaivePaths27(root);
10351
- if (!existsSync49(paths.memoriesDir)) {
10348
+ if (!existsSync50(paths.memoriesDir)) {
10352
10349
  if (opts.json) {
10353
10350
  console.log(JSON.stringify({ error: "not-initialized", root }, null, 2));
10354
10351
  } else {
@@ -10469,7 +10466,7 @@ function applyVerification2(mem, result) {
10469
10466
 
10470
10467
  // src/commands/memory-import.ts
10471
10468
  import { readFile as readFile15 } from "fs/promises";
10472
- import { existsSync as existsSync50 } from "fs";
10469
+ import { existsSync as existsSync51 } from "fs";
10473
10470
  import "commander";
10474
10471
  import {
10475
10472
  findProjectRoot as findProjectRoot31,
@@ -10481,12 +10478,12 @@ function registerMemoryImport(memory2) {
10481
10478
  ).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) => {
10482
10479
  const root = findProjectRoot31(opts.dir);
10483
10480
  const paths = resolveHaivePaths28(root);
10484
- if (!existsSync50(paths.haiveDir)) {
10481
+ if (!existsSync51(paths.haiveDir)) {
10485
10482
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10486
10483
  process.exitCode = 1;
10487
10484
  return;
10488
10485
  }
10489
- if (!existsSync50(opts.from)) {
10486
+ if (!existsSync51(opts.from)) {
10490
10487
  ui.error(`File not found: ${opts.from}`);
10491
10488
  process.exitCode = 1;
10492
10489
  return;
@@ -10519,7 +10516,7 @@ function registerMemoryImport(memory2) {
10519
10516
  }
10520
10517
 
10521
10518
  // src/commands/memory-import-changelog.ts
10522
- import { existsSync as existsSync51 } from "fs";
10519
+ import { existsSync as existsSync53 } from "fs";
10523
10520
  import { readFile as readFile16, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
10524
10521
  import path35 from "path";
10525
10522
  import "commander";
@@ -10594,7 +10591,7 @@ function registerMemoryImportChangelog(memory2) {
10594
10591
  const root = findProjectRoot32(opts.dir);
10595
10592
  const paths = resolveHaivePaths29(root);
10596
10593
  const changelogPath = path35.resolve(root, opts.fromChangelog);
10597
- if (!existsSync51(changelogPath)) {
10594
+ if (!existsSync53(changelogPath)) {
10598
10595
  ui.error(`CHANGELOG not found: ${changelogPath}`);
10599
10596
  process.exitCode = 1;
10600
10597
  return;
@@ -10681,7 +10678,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
10681
10678
  }
10682
10679
 
10683
10680
  // src/commands/memory-digest.ts
10684
- import { existsSync as existsSync53 } from "fs";
10681
+ import { existsSync as existsSync54 } from "fs";
10685
10682
  import { writeFile as writeFile24 } from "fs/promises";
10686
10683
  import path36 from "path";
10687
10684
  import "commander";
@@ -10706,7 +10703,7 @@ function registerMemoryDigest(program2) {
10706
10703
  ).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) => {
10707
10704
  const root = findProjectRoot33(opts.dir);
10708
10705
  const paths = resolveHaivePaths30(root);
10709
- if (!existsSync53(paths.memoriesDir)) {
10706
+ if (!existsSync54(paths.memoriesDir)) {
10710
10707
  ui.error("No .ai/memories found. Run `haive init` first.");
10711
10708
  process.exitCode = 1;
10712
10709
  return;
@@ -10789,7 +10786,7 @@ function registerMemoryDigest(program2) {
10789
10786
 
10790
10787
  // src/commands/session-end.ts
10791
10788
  import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile17, rm as rm2 } from "fs/promises";
10792
- import { existsSync as existsSync54 } from "fs";
10789
+ import { existsSync as existsSync55 } from "fs";
10793
10790
  import { spawn as spawn4 } from "child_process";
10794
10791
  import path37 from "path";
10795
10792
  import "commander";
@@ -10803,7 +10800,7 @@ import {
10803
10800
  } from "@hiveai/core";
10804
10801
  async function buildAutoRecap(paths) {
10805
10802
  const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
10806
- if (!existsSync54(obsFile)) return await buildGitAutoRecap(paths);
10803
+ if (!existsSync55(obsFile)) return await buildGitAutoRecap(paths);
10807
10804
  const raw = await readFile17(obsFile, "utf8").catch(() => "");
10808
10805
  if (!raw.trim()) return await buildGitAutoRecap(paths);
10809
10806
  const lines = raw.split("\n").filter(Boolean);
@@ -10994,7 +10991,7 @@ function registerSessionEnd(session2) {
10994
10991
  ).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) => {
10995
10992
  const root = findProjectRoot34(opts.dir);
10996
10993
  const paths = resolveHaivePaths31(root);
10997
- if (!existsSync54(paths.haiveDir)) {
10994
+ if (!existsSync55(paths.haiveDir)) {
10998
10995
  if (opts.auto || opts.quiet) return;
10999
10996
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11000
10997
  process.exitCode = 1;
@@ -11027,7 +11024,7 @@ function registerSessionEnd(session2) {
11027
11024
  });
11028
11025
  const topic = recapTopic2(scope, opts.module);
11029
11026
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
11030
- const missingPaths = filesTouched.filter((p) => !existsSync54(path37.resolve(root, p)));
11027
+ const missingPaths = filesTouched.filter((p) => !existsSync55(path37.resolve(root, p)));
11031
11028
  if (missingPaths.length > 0 && !opts.quiet) {
11032
11029
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
11033
11030
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
@@ -11035,10 +11032,10 @@ function registerSessionEnd(session2) {
11035
11032
  const cleanupObservations = async () => {
11036
11033
  if (!opts.auto) return;
11037
11034
  const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
11038
- if (existsSync54(obsFile)) await rm2(obsFile).catch(() => {
11035
+ if (existsSync55(obsFile)) await rm2(obsFile).catch(() => {
11039
11036
  });
11040
11037
  };
11041
- if (existsSync54(paths.memoriesDir)) {
11038
+ if (existsSync55(paths.memoriesDir)) {
11042
11039
  const existing = await loadMemoriesFromDir27(paths.memoriesDir);
11043
11040
  const topicMatch = existing.find(
11044
11041
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
@@ -11100,7 +11097,7 @@ function normalizeAnchorPath(root, filePath) {
11100
11097
  }
11101
11098
 
11102
11099
  // src/commands/snapshot.ts
11103
- import { existsSync as existsSync55 } from "fs";
11100
+ import { existsSync as existsSync56 } from "fs";
11104
11101
  import { readdir as readdir4 } from "fs/promises";
11105
11102
  import path38 from "path";
11106
11103
  import "commander";
@@ -11135,14 +11132,14 @@ function registerSnapshot(program2) {
11135
11132
  ).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) => {
11136
11133
  const root = findProjectRoot35(opts.dir);
11137
11134
  const paths = resolveHaivePaths32(root);
11138
- if (!existsSync55(paths.haiveDir)) {
11135
+ if (!existsSync56(paths.haiveDir)) {
11139
11136
  ui.error("No .ai/ found. Run `haive init` first.");
11140
11137
  process.exitCode = 1;
11141
11138
  return;
11142
11139
  }
11143
11140
  if (opts.list) {
11144
11141
  const contractsDir = path38.join(paths.haiveDir, "contracts");
11145
- if (!existsSync55(contractsDir)) {
11142
+ if (!existsSync56(contractsDir)) {
11146
11143
  console.log(ui.dim("No contract snapshots found."));
11147
11144
  return;
11148
11145
  }
@@ -11266,7 +11263,7 @@ function detectFormat(filePath) {
11266
11263
  }
11267
11264
 
11268
11265
  // src/commands/hub.ts
11269
- import { existsSync as existsSync56 } from "fs";
11266
+ import { existsSync as existsSync57 } from "fs";
11270
11267
  import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile26, copyFile } from "fs/promises";
11271
11268
  import path39 from "path";
11272
11269
  import { spawnSync as spawnSync5 } from "child_process";
@@ -11368,7 +11365,7 @@ Next steps:
11368
11365
  return;
11369
11366
  }
11370
11367
  const hubRoot = path39.resolve(root, config.hubPath);
11371
- if (!existsSync56(hubRoot)) {
11368
+ if (!existsSync57(hubRoot)) {
11372
11369
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
11373
11370
  process.exitCode = 1;
11374
11371
  return;
@@ -11438,7 +11435,7 @@ Next steps:
11438
11435
  }
11439
11436
  const hubRoot = path39.resolve(root, config.hubPath);
11440
11437
  const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
11441
- if (!existsSync56(hubSharedDir)) {
11438
+ if (!existsSync57(hubSharedDir)) {
11442
11439
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
11443
11440
  return;
11444
11441
  }
@@ -11493,7 +11490,7 @@ Next steps:
11493
11490
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
11494
11491
  );
11495
11492
  const sharedDir = path39.join(paths.memoriesDir, "shared");
11496
- if (existsSync56(sharedDir)) {
11493
+ if (existsSync57(sharedDir)) {
11497
11494
  const { readdir: readdir7 } = await import("fs/promises");
11498
11495
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
11499
11496
  console.log(`
@@ -11522,7 +11519,7 @@ Next steps:
11522
11519
 
11523
11520
  // src/commands/stats.ts
11524
11521
  import "commander";
11525
- import { existsSync as existsSync57 } from "fs";
11522
+ import { existsSync as existsSync58 } from "fs";
11526
11523
  import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
11527
11524
  import path40 from "path";
11528
11525
  import {
@@ -11600,7 +11597,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11600
11597
  const size = await usageLogSize(paths);
11601
11598
  let events = await readUsageEvents2(paths);
11602
11599
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
11603
- if (existsSync57(paths.memoriesDir)) {
11600
+ if (existsSync58(paths.memoriesDir)) {
11604
11601
  const mems = await loadMemoriesFromDir29(paths.memoriesDir);
11605
11602
  for (const { memory: memory2 } of mems) {
11606
11603
  const fm = memory2.frontmatter;
@@ -11820,7 +11817,7 @@ function summarize(name, t0, payload, notes) {
11820
11817
  }
11821
11818
 
11822
11819
  // src/commands/benchmark.ts
11823
- import { existsSync as existsSync58 } from "fs";
11820
+ import { existsSync as existsSync59 } from "fs";
11824
11821
  import { readdir as readdir5, readFile as readFile19, writeFile as writeFile28 } from "fs/promises";
11825
11822
  import path41 from "path";
11826
11823
  import "commander";
@@ -11868,14 +11865,14 @@ function resolveBenchmarkRoot(dir) {
11868
11865
  return path41.join(projectRoot, candidate);
11869
11866
  }
11870
11867
  async function collectRows(root) {
11871
- if (!existsSync58(root)) throw new Error(`Benchmark directory not found: ${root}`);
11868
+ if (!existsSync59(root)) throw new Error(`Benchmark directory not found: ${root}`);
11872
11869
  const entries = await readdir5(root, { withFileTypes: true });
11873
11870
  const rows = [];
11874
11871
  for (const entry of entries) {
11875
11872
  if (!entry.isDirectory()) continue;
11876
11873
  const fixtureDir = path41.join(root, entry.name);
11877
11874
  const reportFile = path41.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
11878
- if (!existsSync58(reportFile)) continue;
11875
+ if (!existsSync59(reportFile)) continue;
11879
11876
  const report = await readFile19(reportFile, "utf8");
11880
11877
  rows.push(parseAgentReport(entry.name, report));
11881
11878
  }
@@ -11966,7 +11963,7 @@ function escapeRegExp(value) {
11966
11963
 
11967
11964
  // src/commands/memory-suggest.ts
11968
11965
  import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
11969
- import { existsSync as existsSync59 } from "fs";
11966
+ import { existsSync as existsSync60 } from "fs";
11970
11967
  import path43 from "path";
11971
11968
  import "commander";
11972
11969
  import {
@@ -12045,7 +12042,7 @@ function registerMemorySuggest(memory2) {
12045
12042
  }
12046
12043
  const created = [];
12047
12044
  const skipped = [];
12048
- const existing = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
12045
+ const existing = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
12049
12046
  for (const s of top) {
12050
12047
  const slug = slugify2(s.query);
12051
12048
  if (!slug) {
@@ -12069,7 +12066,7 @@ function registerMemorySuggest(memory2) {
12069
12066
  const body = renderTemplate(s, fm.id, status);
12070
12067
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
12071
12068
  await mkdir18(path43.dirname(file), { recursive: true });
12072
- if (existsSync59(file)) {
12069
+ if (existsSync60(file)) {
12073
12070
  skipped.push({ query: s.query, reason: `file already exists at ${path43.relative(root, file)}` });
12074
12071
  continue;
12075
12072
  }
@@ -12172,7 +12169,7 @@ function truncate2(text, max) {
12172
12169
  }
12173
12170
 
12174
12171
  // src/commands/memory-archive.ts
12175
- import { existsSync as existsSync60 } from "fs";
12172
+ import { existsSync as existsSync61 } from "fs";
12176
12173
  import { writeFile as writeFile30 } from "fs/promises";
12177
12174
  import path44 from "path";
12178
12175
  import "commander";
@@ -12193,7 +12190,7 @@ function registerMemoryArchive(memory2) {
12193
12190
  ).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) => {
12194
12191
  const root = findProjectRoot41(opts.dir);
12195
12192
  const paths = resolveHaivePaths37(root);
12196
- if (!existsSync60(paths.memoriesDir)) {
12193
+ if (!existsSync61(paths.memoriesDir)) {
12197
12194
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
12198
12195
  process.exitCode = 1;
12199
12196
  return;
@@ -12218,7 +12215,7 @@ function registerMemoryArchive(memory2) {
12218
12215
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
12219
12216
  const retired = retirementSignal2(fm, mem.body);
12220
12217
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
12221
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync60(path44.join(paths.root, p)));
12218
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync61(path44.join(paths.root, p)));
12222
12219
  const isAnchorless = !hasAnyAnchor;
12223
12220
  if (!retired.retired && !opts.unread && !isAnchorless && !allPathsGone) continue;
12224
12221
  const u = getUsage18(usage, fm.id);
@@ -12293,7 +12290,7 @@ function parseDays(input) {
12293
12290
  }
12294
12291
 
12295
12292
  // src/commands/doctor.ts
12296
- import { existsSync as existsSync61, statSync as statSync2 } from "fs";
12293
+ import { existsSync as existsSync63, statSync as statSync2 } from "fs";
12297
12294
  import { readFile as readFile20, stat, writeFile as writeFile31 } from "fs/promises";
12298
12295
  import path45 from "path";
12299
12296
  import { execFileSync, execSync as execSync3 } from "child_process";
@@ -12319,7 +12316,7 @@ function registerDoctor(program2) {
12319
12316
  const findings = [];
12320
12317
  const repairs = [];
12321
12318
  const config = await loadConfig11(paths);
12322
- if (!existsSync61(paths.haiveDir)) {
12319
+ if (!existsSync63(paths.haiveDir)) {
12323
12320
  findings.push({
12324
12321
  severity: "error",
12325
12322
  code: "not-initialized",
@@ -12340,7 +12337,7 @@ function registerDoctor(program2) {
12340
12337
  })
12341
12338
  );
12342
12339
  }
12343
- if (!existsSync61(paths.projectContext)) {
12340
+ if (!existsSync63(paths.projectContext)) {
12344
12341
  findings.push({
12345
12342
  severity: "warn",
12346
12343
  code: "no-project-context",
@@ -12369,7 +12366,7 @@ function registerDoctor(program2) {
12369
12366
  });
12370
12367
  }
12371
12368
  }
12372
- const memories = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
12369
+ const memories = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
12373
12370
  const now = Date.now();
12374
12371
  if (memories.length === 0) {
12375
12372
  findings.push({
@@ -12512,7 +12509,7 @@ function registerDoctor(program2) {
12512
12509
  if (config.enforcement?.requireBriefingFirst) {
12513
12510
  const claudeSettings = path45.join(root, ".claude", "settings.local.json");
12514
12511
  let hasClaudeEnforcement = false;
12515
- if (existsSync61(claudeSettings)) {
12512
+ if (existsSync63(claudeSettings)) {
12516
12513
  try {
12517
12514
  const { readFile: readFile24 } = await import("fs/promises");
12518
12515
  const raw = await readFile24(claudeSettings, "utf8");
@@ -12538,14 +12535,14 @@ function registerDoctor(program2) {
12538
12535
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12539
12536
  });
12540
12537
  }
12541
- findings.push(...await collectInstallFindings(root, "0.10.5"));
12538
+ findings.push(...await collectInstallFindings(root, "0.10.8"));
12542
12539
  try {
12543
12540
  const legacyRaw = execSync3("haive-mcp --version", {
12544
12541
  encoding: "utf8",
12545
12542
  timeout: 3e3,
12546
12543
  stdio: ["ignore", "pipe", "ignore"]
12547
12544
  }).trim();
12548
- const cliVersion = "0.10.5";
12545
+ const cliVersion = "0.10.8";
12549
12546
  if (legacyRaw && legacyRaw !== cliVersion) {
12550
12547
  findings.push({
12551
12548
  severity: "warn",
@@ -12567,7 +12564,7 @@ npm uninstall -g @hiveai/mcp`
12567
12564
  ];
12568
12565
  const staleConfigs = [];
12569
12566
  for (const cfgPath of configPaths) {
12570
- if (!existsSync61(cfgPath)) continue;
12567
+ if (!existsSync63(cfgPath)) continue;
12571
12568
  try {
12572
12569
  const raw = await readFile20(cfgPath, "utf8");
12573
12570
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
@@ -12862,7 +12859,7 @@ which -a haive`
12862
12859
  ];
12863
12860
  for (const rel of integrationFiles) {
12864
12861
  const file = path45.join(root, rel);
12865
- if (!existsSync61(file)) continue;
12862
+ if (!existsSync63(file)) continue;
12866
12863
  const text = await readFile20(file, "utf8").catch(() => "");
12867
12864
  for (const bin of extractAbsoluteHaiveBins(text)) {
12868
12865
  const version = versionForBinary(bin);
@@ -12958,7 +12955,7 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
12958
12955
  }
12959
12956
  }
12960
12957
  async function readJson(file) {
12961
- if (!existsSync61(file)) return null;
12958
+ if (!existsSync63(file)) return null;
12962
12959
  try {
12963
12960
  return JSON.parse(await readFile20(file, "utf8"));
12964
12961
  } catch {
@@ -13005,7 +13002,7 @@ function extractAbsoluteHaiveBins(text) {
13005
13002
  }
13006
13003
 
13007
13004
  // src/commands/playback.ts
13008
- import { existsSync as existsSync63 } from "fs";
13005
+ import { existsSync as existsSync64 } from "fs";
13009
13006
  import "commander";
13010
13007
  import {
13011
13008
  findProjectRoot as findProjectRoot43,
@@ -13035,7 +13032,7 @@ function registerPlayback(program2) {
13035
13032
  const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
13036
13033
  const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
13037
13034
  const sessions = bucketSessions(filtered, gapMs);
13038
- const all = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
13035
+ const all = existsSync64(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
13039
13036
  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);
13040
13037
  const enriched = sessions.map((s, i) => {
13041
13038
  const startMs = Date.parse(s.start);
@@ -13277,7 +13274,7 @@ function runCommand3(cmd, args, cwd) {
13277
13274
  }
13278
13275
 
13279
13276
  // src/commands/welcome.ts
13280
- import { existsSync as existsSync64 } from "fs";
13277
+ import { existsSync as existsSync65 } from "fs";
13281
13278
  import "commander";
13282
13279
  import {
13283
13280
  findProjectRoot as findProjectRoot45,
@@ -13299,7 +13296,7 @@ function registerWelcome(program2) {
13299
13296
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
13300
13297
  const root = findProjectRoot45(opts.dir);
13301
13298
  const paths = resolveHaivePaths41(root);
13302
- if (!existsSync64(paths.memoriesDir)) {
13299
+ if (!existsSync65(paths.memoriesDir)) {
13303
13300
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
13304
13301
  process.exitCode = 1;
13305
13302
  return;
@@ -13371,7 +13368,7 @@ function registerResolveProject(program2) {
13371
13368
  }
13372
13369
 
13373
13370
  // src/commands/runtime-journal.ts
13374
- import { existsSync as existsSync65 } from "fs";
13371
+ import { existsSync as existsSync66 } from "fs";
13375
13372
  import path47 from "path";
13376
13373
  import "commander";
13377
13374
  import {
@@ -13397,7 +13394,7 @@ function registerRuntime(program2) {
13397
13394
  const root = path47.resolve(opts.dir ?? process.cwd());
13398
13395
  const paths = resolveHaivePaths42(findProjectRoot46(root));
13399
13396
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
13400
- if (!existsSync65(paths.haiveDir)) {
13397
+ if (!existsSync66(paths.haiveDir)) {
13401
13398
  ui.error("No .ai/ \u2014 run `haive init` first.");
13402
13399
  process.exitCode = 1;
13403
13400
  return;
@@ -13412,7 +13409,7 @@ function registerRuntime(program2) {
13412
13409
  }
13413
13410
 
13414
13411
  // src/commands/memory-timeline.ts
13415
- import { existsSync as existsSync66 } from "fs";
13412
+ import { existsSync as existsSync67 } from "fs";
13416
13413
  import path48 from "path";
13417
13414
  import "commander";
13418
13415
  import {
@@ -13431,7 +13428,7 @@ function registerMemoryTimeline(memory2) {
13431
13428
  }
13432
13429
  const root = path48.resolve(opts.dir ?? process.cwd());
13433
13430
  const paths = resolveHaivePaths43(findProjectRoot47(root));
13434
- if (!existsSync66(paths.memoriesDir)) {
13431
+ if (!existsSync67(paths.memoriesDir)) {
13435
13432
  ui.error("No memories \u2014 run `haive init`.");
13436
13433
  process.exitCode = 1;
13437
13434
  return;
@@ -13449,7 +13446,7 @@ function registerMemoryTimeline(memory2) {
13449
13446
  }
13450
13447
 
13451
13448
  // src/commands/memory-conflict-candidates.ts
13452
- import { existsSync as existsSync67 } from "fs";
13449
+ import { existsSync as existsSync68 } from "fs";
13453
13450
  import path49 from "path";
13454
13451
  import "commander";
13455
13452
  import {
@@ -13474,7 +13471,7 @@ function registerMemoryConflictCandidates(memory2) {
13474
13471
  ).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) => {
13475
13472
  const root = path49.resolve(opts.dir ?? process.cwd());
13476
13473
  const paths = resolveHaivePaths44(findProjectRoot48(root));
13477
- if (!existsSync67(paths.memoriesDir)) {
13474
+ if (!existsSync68(paths.memoriesDir)) {
13478
13475
  ui.error("No memories \u2014 run `haive init`.");
13479
13476
  process.exitCode = 1;
13480
13477
  return;
@@ -13510,14 +13507,14 @@ function registerMemoryConflictCandidates(memory2) {
13510
13507
 
13511
13508
  // src/commands/enforce.ts
13512
13509
  import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
13513
- import { existsSync as existsSync68, statSync as statSync3 } from "fs";
13510
+ import { existsSync as existsSync69, statSync as statSync3 } from "fs";
13514
13511
  import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
13515
13512
  import path50 from "path";
13516
13513
  import "commander";
13517
13514
  import {
13518
13515
  antiPatternGateParams as antiPatternGateParams2,
13519
13516
  findProjectRoot as findProjectRoot49,
13520
- hasRecentBriefingMarker,
13517
+ hasRecentBriefingMarker as hasRecentBriefingMarker2,
13521
13518
  isFreshIsoDate,
13522
13519
  loadConfig as loadConfig13,
13523
13520
  loadMemoriesFromDir as loadMemoriesFromDir36,
@@ -13585,14 +13582,14 @@ function registerEnforce(program2) {
13585
13582
  const root = findProjectRoot49(opts.dir);
13586
13583
  const paths = resolveHaivePaths45(root);
13587
13584
  const cacheDir = path50.join(paths.haiveDir, ".cache");
13588
- if (existsSync68(cacheDir)) {
13585
+ if (existsSync69(cacheDir)) {
13589
13586
  if (opts.dryRun) ui.info(`would clean ${path50.relative(root, cacheDir)} (preserving .gitignore)`);
13590
13587
  else {
13591
13588
  const removed = await cleanupCacheDir(cacheDir);
13592
13589
  ui.success(`cleaned ${path50.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13593
13590
  }
13594
13591
  }
13595
- if (existsSync68(paths.runtimeDir)) {
13592
+ if (existsSync69(paths.runtimeDir)) {
13596
13593
  if (opts.dryRun) ui.info(`would clean ${path50.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
13597
13594
  else {
13598
13595
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
@@ -13610,7 +13607,7 @@ function registerEnforce(program2) {
13610
13607
  const root = resolveRoot(opts.dir, payload);
13611
13608
  if (!root) return;
13612
13609
  const paths = resolveHaivePaths45(root);
13613
- if (!existsSync68(paths.haiveDir)) return;
13610
+ if (!existsSync69(paths.haiveDir)) return;
13614
13611
  await mkdir19(paths.runtimeDir, { recursive: true });
13615
13612
  const sessionId = opts.sessionId ?? payload.session_id;
13616
13613
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
@@ -13673,9 +13670,9 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13673
13670
  const root = resolveRoot(opts.dir, payload);
13674
13671
  if (!root) return;
13675
13672
  const paths = resolveHaivePaths45(root);
13676
- if (!existsSync68(paths.haiveDir)) return;
13673
+ if (!existsSync69(paths.haiveDir)) return;
13677
13674
  if (!isWriteLikeTool(payload)) return;
13678
- const ok = await hasRecentBriefingMarker(paths, payload.session_id);
13675
+ const ok = await hasRecentBriefingMarker2(paths, payload.session_id);
13679
13676
  if (ok) {
13680
13677
  const targetFiles = extractToolPaths(payload, root);
13681
13678
  if (targetFiles.length === 0) return;
@@ -13717,7 +13714,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13717
13714
  async function runWithEnforcement(command, args, opts) {
13718
13715
  const root = findProjectRoot49(opts.dir);
13719
13716
  const paths = resolveHaivePaths45(root);
13720
- if (!existsSync68(paths.haiveDir)) {
13717
+ if (!existsSync69(paths.haiveDir)) {
13721
13718
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
13722
13719
  process.exit(1);
13723
13720
  }
@@ -13812,7 +13809,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
13812
13809
  async function buildEnforcementReport(dir, stage, sessionId) {
13813
13810
  const root = findProjectRoot49(dir);
13814
13811
  const paths = resolveHaivePaths45(root);
13815
- const initialized = existsSync68(paths.haiveDir);
13812
+ const initialized = existsSync69(paths.haiveDir);
13816
13813
  const config = initialized ? await loadConfig13(paths) : {};
13817
13814
  if (initialized) await applyLightweightRepairs(root, paths);
13818
13815
  const mode = config.enforcement?.mode ?? "strict";
@@ -13843,9 +13840,9 @@ async function buildEnforcementReport(dir, stage, sessionId) {
13843
13840
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
13844
13841
  });
13845
13842
  }
13846
- findings.push(...await inspectIntegrationVersions(root, "0.10.5"));
13843
+ findings.push(...await inspectIntegrationVersions(root, "0.10.8"));
13847
13844
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
13848
- const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
13845
+ const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
13849
13846
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
13850
13847
  severity: "error",
13851
13848
  code: "briefing-missing",
@@ -13913,7 +13910,7 @@ function withCategories(report) {
13913
13910
  };
13914
13911
  }
13915
13912
  async function hasRecentSessionRecap(paths) {
13916
- if (!existsSync68(paths.memoriesDir)) return false;
13913
+ if (!existsSync69(paths.memoriesDir)) return false;
13917
13914
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13918
13915
  return all.some(({ memory: memory2 }) => {
13919
13916
  const fm = memory2.frontmatter;
@@ -13922,7 +13919,7 @@ async function hasRecentSessionRecap(paths) {
13922
13919
  });
13923
13920
  }
13924
13921
  async function verifyMemoryPolicy(paths, config) {
13925
- if (!existsSync68(paths.memoriesDir)) return [];
13922
+ if (!existsSync69(paths.memoriesDir)) return [];
13926
13923
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13927
13924
  const findings = [];
13928
13925
  const staleImportant = [];
@@ -13960,7 +13957,7 @@ async function verifyMemoryPolicy(paths, config) {
13960
13957
  return findings;
13961
13958
  }
13962
13959
  async function verifyDecisionCoverage(paths, stage, sessionId) {
13963
- if (!existsSync68(paths.memoriesDir)) return [];
13960
+ if (!existsSync69(paths.memoriesDir)) return [];
13964
13961
  const changedFiles = await getChangedFiles(paths.root, stage);
13965
13962
  if (changedFiles.length === 0) {
13966
13963
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
@@ -14071,7 +14068,7 @@ async function cleanupRuntimeDir(runtimeDir) {
14071
14068
  removed++;
14072
14069
  }
14073
14070
  await writeFile33(path50.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
14074
- if (!existsSync68(path50.join(runtimeDir, "README.md"))) {
14071
+ if (!existsSync69(path50.join(runtimeDir, "README.md"))) {
14075
14072
  await writeFile33(
14076
14073
  path50.join(runtimeDir, "README.md"),
14077
14074
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
@@ -14114,7 +14111,7 @@ async function inspectIntegrationVersions(root, expectedVersion) {
14114
14111
  const findings = [];
14115
14112
  for (const rel of files) {
14116
14113
  const file = path50.join(root, rel);
14117
- if (!existsSync68(file)) continue;
14114
+ if (!existsSync69(file)) continue;
14118
14115
  const text = await readFile21(file, "utf8").catch(() => "");
14119
14116
  for (const bin of extractAbsoluteHaiveBins2(text)) {
14120
14117
  const version = versionForBinary2(bin);
@@ -14210,7 +14207,7 @@ function buildScore(findings, threshold = 80) {
14210
14207
  }
14211
14208
  async function installGitEnforcement(root) {
14212
14209
  const hooksDir = path50.join(root, ".git", "hooks");
14213
- if (!existsSync68(path50.join(root, ".git"))) {
14210
+ if (!existsSync69(path50.join(root, ".git"))) {
14214
14211
  ui.warn("No .git directory found; git enforcement hooks skipped.");
14215
14212
  return;
14216
14213
  }
@@ -14233,7 +14230,7 @@ haive enforce check --stage pre-push --dir . || exit $?
14233
14230
  ];
14234
14231
  for (const hook of hooks) {
14235
14232
  const file = path50.join(hooksDir, hook.name);
14236
- if (existsSync68(file)) {
14233
+ if (existsSync69(file)) {
14237
14234
  const current = await readFile21(file, "utf8").catch(() => "");
14238
14235
  if (current.includes(ENFORCE_HOOK_MARKER)) {
14239
14236
  await writeFile33(file, hook.body, "utf8");
@@ -14252,7 +14249,7 @@ ${hook.body}`, "utf8");
14252
14249
  async function installCiEnforcement(root) {
14253
14250
  const workflowPath = path50.join(root, ".github", "workflows", "haive-enforcement.yml");
14254
14251
  await mkdir19(path50.dirname(workflowPath), { recursive: true });
14255
- if (existsSync68(workflowPath)) {
14252
+ if (existsSync69(workflowPath)) {
14256
14253
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
14257
14254
  return;
14258
14255
  }
@@ -14381,7 +14378,7 @@ function normalizeToolPath(file, root) {
14381
14378
  return path50.relative(root, normalized).replace(/\\/g, "/");
14382
14379
  }
14383
14380
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
14384
- if (!existsSync68(paths.memoriesDir)) return [];
14381
+ if (!existsSync69(paths.memoriesDir)) return [];
14385
14382
  const marker = await readRecentBriefingMarker(paths, sessionId);
14386
14383
  const consulted = new Set(marker?.memory_ids ?? []);
14387
14384
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
@@ -14455,7 +14452,7 @@ function registerRun(program2) {
14455
14452
 
14456
14453
  // src/commands/sensors.ts
14457
14454
  import { execFile as execFile2 } from "child_process";
14458
- import { existsSync as existsSync69 } from "fs";
14455
+ import { existsSync as existsSync70 } from "fs";
14459
14456
  import { chmod as chmod3, mkdir as mkdir20, readFile as readFile23, writeFile as writeFile34 } from "fs/promises";
14460
14457
  import path51 from "path";
14461
14458
  import { promisify as promisify2 } from "util";
@@ -14538,7 +14535,7 @@ function registerSensors(program2) {
14538
14535
  }
14539
14536
  const root = findProjectRoot50(opts.dir);
14540
14537
  const paths = resolveHaivePaths46(root);
14541
- const loaded = existsSync69(paths.memoriesDir) ? await loadMemoriesFromDir37(paths.memoriesDir) : [];
14538
+ const loaded = existsSync70(paths.memoriesDir) ? await loadMemoriesFromDir37(paths.memoriesDir) : [];
14542
14539
  const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
14543
14540
  if (!found) {
14544
14541
  ui.error(`No memory found with id ${id}`);
@@ -14600,7 +14597,7 @@ async function sensorRows(paths) {
14600
14597
  });
14601
14598
  }
14602
14599
  async function runnableSensorMemories(paths, regexOnly = true) {
14603
- if (!existsSync69(paths.memoriesDir)) return [];
14600
+ if (!existsSync70(paths.memoriesDir)) return [];
14604
14601
  const loaded = await loadMemoriesFromDir37(paths.memoriesDir);
14605
14602
  return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
14606
14603
  const sensor = memory2.frontmatter.sensor;
@@ -14643,7 +14640,7 @@ function shellQuote(value) {
14643
14640
 
14644
14641
  // src/index.ts
14645
14642
  var program = new Command53();
14646
- program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.10.5").option("--advanced", "show maintenance and experimental commands in help");
14643
+ program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.10.8").option("--advanced", "show maintenance and experimental commands in help");
14647
14644
  registerInit(program);
14648
14645
  registerWelcome(program);
14649
14646
  registerResolveProject(program);