@hiveai/cli 0.9.18 → 0.9.20

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
@@ -3294,8 +3294,8 @@ async function memList(input, ctx) {
3294
3294
  return { memories };
3295
3295
  }
3296
3296
  var MemSaveInputSchema = {
3297
- type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "attempt", "session_recap"]).describe(
3298
- "Kind of memory being saved. Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
3297
+ type: z4.enum(["convention", "decision", "gotcha", "architecture", "glossary", "skill", "attempt", "session_recap"]).describe(
3298
+ "Kind of memory being saved. Use 'skill' for reusable procedures/playbooks agents should follow for recurring tasks (feedforward harness guide). Use 'attempt' for failed approaches (auto-validated). Use 'session_recap' via mem_session_end instead."
3299
3299
  ),
3300
3300
  slug: z4.string().min(1).describe("Short human-readable identifier \u2014 becomes part of the filename"),
3301
3301
  body: z4.string().describe("Markdown body of the memory"),
@@ -4895,10 +4895,10 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
4895
4895
  );
4896
4896
  const strongSemantic = (memory2.semantic_score ?? 0) >= 0.65;
4897
4897
  const usefulSemantic = (memory2.semantic_score ?? 0) >= 0.35;
4898
- if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic)) {
4898
+ if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
4899
4899
  return "must_read";
4900
4900
  }
4901
- if (memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
4901
+ if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
4902
4902
  return "useful";
4903
4903
  }
4904
4904
  return "background";
@@ -4978,6 +4978,7 @@ function explainWhySurfaced(memory2, loaded, inputFiles, inferredModules) {
4978
4978
  }
4979
4979
  why.push(`Confidence: ${memory2.confidence}; read ${memory2.read_count} time${memory2.read_count === 1 ? "" : "s"}.`);
4980
4980
  if (memory2.type === "attempt") why.push("Failed-approach record; read before repeating the same path.");
4981
+ if (memory2.type === "skill") why.push("Skill (reusable procedure/playbook) \u2014 follow the steps described when doing this type of task.");
4981
4982
  if (memory2.status === "proposed" || memory2.status === "draft") {
4982
4983
  why.push("Unvalidated record; use cautiously or ask a human before treating it as policy.");
4983
4984
  }
@@ -6515,7 +6516,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
6515
6516
  };
6516
6517
  }
6517
6518
  var SERVER_NAME = "haive";
6518
- var SERVER_VERSION = "0.9.18";
6519
+ var SERVER_VERSION = "0.9.20";
6519
6520
  function jsonResult(data) {
6520
6521
  return {
6521
6522
  content: [
@@ -6535,6 +6536,7 @@ var ENFORCEMENT_PROFILE_TOOLS = [
6535
6536
  "mem_verify",
6536
6537
  "mem_relevant_to",
6537
6538
  "code_map",
6539
+ "code_search",
6538
6540
  "pre_commit_check",
6539
6541
  "mem_session_end"
6540
6542
  ];
@@ -8495,6 +8497,7 @@ function registerMemoryAdd(memory2) {
8495
8497
  `Save a piece of knowledge as a persistent memory.
8496
8498
 
8497
8499
  Memory types:
8500
+ skill \u2014 reusable procedure/playbook agents follow for a recurring task (e.g. deploy, review)
8498
8501
  convention \u2014 how things are done here (naming, patterns, tooling)
8499
8502
  decision \u2014 a choice made and WHY (tradeoffs, constraints)
8500
8503
  gotcha \u2014 non-obvious behavior that surprises newcomers
@@ -8514,7 +8517,7 @@ function registerMemoryAdd(memory2) {
8514
8517
  haive memory add --type convention --slug flyway-no-modify --topic flyway \\\\
8515
8518
  --scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
8516
8519
  `
8517
- ).requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").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("--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) => {
8520
+ ).requiredOption("--type <type>", "skill | convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").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("--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) => {
8518
8521
  const root = findProjectRoot13(opts.dir);
8519
8522
  const paths = resolveHaivePaths10(root);
8520
8523
  if (!existsSync33(paths.haiveDir)) {
@@ -8638,7 +8641,8 @@ TODO \u2014 write the memory body.
8638
8641
  if (inferredTags.length > 0) {
8639
8642
  ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
8640
8643
  }
8641
- if (anchorPaths.length === 0) {
8644
+ const typeNeedsAnchor = !["skill", "glossary", "session_recap"].includes(opts.type);
8645
+ if (anchorPaths.length === 0 && typeNeedsAnchor) {
8642
8646
  ui.warn(
8643
8647
  `This memory has no anchor paths \u2014 staleness cannot be detected automatically.
8644
8648
  Add file anchors: haive memory update ${frontmatter.id} --paths <file1,file2>`
@@ -11145,6 +11149,7 @@ import {
11145
11149
  aggregateUsage as aggregateUsage2,
11146
11150
  buildFrontmatter as buildFrontmatter11,
11147
11151
  findProjectRoot as findProjectRoot39,
11152
+ loadConfig as loadConfig9,
11148
11153
  loadMemoriesFromDir as loadMemoriesFromDir31,
11149
11154
  memoryFilePath as memoryFilePath10,
11150
11155
  parseSince as parseSince2,
@@ -11158,10 +11163,14 @@ var SEARCH_TOOLS = /* @__PURE__ */ new Set([
11158
11163
  "mem_relevant_to",
11159
11164
  "get_briefing"
11160
11165
  ]);
11166
+ var SYNTHETIC_QUERY_RE = /\b(auto-promote-marker|local enforcement smoke|cli-test-session)\b/i;
11167
+ function isSyntheticSuggestionQuery(query) {
11168
+ return SYNTHETIC_QUERY_RE.test(query);
11169
+ }
11161
11170
  function registerMemorySuggest(memory2) {
11162
11171
  memory2.command("suggest").description(
11163
- "Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to draft the top-N suggestions as draft memories. They land\n in personal scope by default with status=draft, ready for you to edit and promote."
11164
- ).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of drafted memories (personal | team)", "personal").option("--auto-save", "draft top-N suggestions as draft memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11172
+ "Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to save the top-N suggestions using the project defaults.\n In autopilot, suggestions land as validated team records; in manual mode they stay draft."
11173
+ ).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of saved memories (personal | team; default: config default)").option("--auto-save", "save top-N suggestions as memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11165
11174
  const root = findProjectRoot39(opts.dir);
11166
11175
  const paths = resolveHaivePaths35(root);
11167
11176
  const events = await readUsageEvents3(paths);
@@ -11182,6 +11191,7 @@ function registerMemorySuggest(memory2) {
11182
11191
  if (!SEARCH_TOOLS.has(e.tool)) continue;
11183
11192
  const key = (e.summary ?? "").toLowerCase().trim();
11184
11193
  if (!key) continue;
11194
+ if (isSyntheticSuggestionQuery(key)) continue;
11185
11195
  const prior = queries.get(key);
11186
11196
  if (prior) {
11187
11197
  prior.count++;
@@ -11200,11 +11210,13 @@ function registerMemorySuggest(memory2) {
11200
11210
  inferred_type: inferType(v.tools, query)
11201
11211
  })).sort((a, b) => b.count - a.count);
11202
11212
  if (opts.autoSave) {
11213
+ const config = await loadConfig9(paths);
11203
11214
  const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
11204
- const scope = opts.scope === "team" ? "team" : "personal";
11215
+ const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
11216
+ const status = config.defaultStatus === "validated" ? "validated" : "draft";
11205
11217
  const top = suggestions.slice(0, topN);
11206
11218
  if (top.length === 0) {
11207
- ui.warn(`No suggestions met --min=${minCount} \u2014 nothing to draft.`);
11219
+ ui.warn(`No suggestions met --min=${minCount} \u2014 nothing to save.`);
11208
11220
  return;
11209
11221
  }
11210
11222
  const created = [];
@@ -11227,10 +11239,10 @@ function registerMemorySuggest(memory2) {
11227
11239
  scope,
11228
11240
  tags: ["auto-suggested", ...s.tools],
11229
11241
  paths: [],
11230
- symbols: []
11242
+ symbols: [],
11243
+ status
11231
11244
  });
11232
- fm.status = "draft";
11233
- const body = renderTemplate(s);
11245
+ const body = renderTemplate(s, fm.id, status);
11234
11246
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
11235
11247
  await mkdir18(path41.dirname(file), { recursive: true });
11236
11248
  if (existsSync60(file)) {
@@ -11245,7 +11257,7 @@ function registerMemorySuggest(memory2) {
11245
11257
  return;
11246
11258
  }
11247
11259
  for (const c of created) {
11248
- ui.success(`Drafted ${c.id} \u2192 ${c.file}`);
11260
+ ui.success(`${status === "validated" ? "Saved" : "Drafted"} ${c.id} \u2192 ${c.file}`);
11249
11261
  console.log(` ${ui.dim("from query:")} ${truncate2(c.query, 60)}`);
11250
11262
  }
11251
11263
  for (const s of skipped) {
@@ -11253,7 +11265,11 @@ function registerMemorySuggest(memory2) {
11253
11265
  }
11254
11266
  if (created.length > 0) {
11255
11267
  console.log();
11256
- ui.info("Drafts are status=draft \u2014 edit them, then `haive memory promote` to validate.");
11268
+ if (status === "validated") {
11269
+ ui.info("Autopilot defaults applied: suggestions are status=validated and active.");
11270
+ } else {
11271
+ ui.info("Drafts are status=draft \u2014 edit them, then run `haive memory promote <id>`.");
11272
+ }
11257
11273
  }
11258
11274
  return;
11259
11275
  }
@@ -11278,7 +11294,7 @@ function registerMemorySuggest(memory2) {
11278
11294
  console.log(` ${ui.dim("\u2192")} ${s.reason}`);
11279
11295
  }
11280
11296
  console.log();
11281
- ui.info("Run with --auto-save to draft the top-3 as draft memories.");
11297
+ ui.info("Run with --auto-save to save the top-3 using the project defaults.");
11282
11298
  });
11283
11299
  }
11284
11300
  function chooseReason(tools, count) {
@@ -11299,7 +11315,8 @@ function inferType(tools, query) {
11299
11315
  }
11300
11316
  return "convention";
11301
11317
  }
11302
- function renderTemplate(s) {
11318
+ function renderTemplate(s, id, status) {
11319
+ const nextStep = status === "validated" ? `This record is already active because project autopilot defaults set status=validated. Replace the template body with the actual answer when known.` : `Then run \`haive memory promote ${id}\` to move it into team review.`;
11303
11320
  return [
11304
11321
  `# Auto-drafted from recurring searches`,
11305
11322
  ``,
@@ -11319,7 +11336,7 @@ function renderTemplate(s) {
11319
11336
  `- **Why** \u2014 the rationale or root cause`,
11320
11337
  `- **How to apply** \u2014 what an agent should do when this comes up again`,
11321
11338
  ``,
11322
- `Then run \`haive memory promote ${truncate2(s.query, 30)}\` to mark it validated.`
11339
+ nextStep
11323
11340
  ].join("\n");
11324
11341
  }
11325
11342
  function slugify(s) {
@@ -11455,7 +11472,7 @@ import {
11455
11472
  findProjectRoot as findProjectRoot41,
11456
11473
  getUsage as getUsage20,
11457
11474
  loadCodeMap as loadCodeMap7,
11458
- loadConfig as loadConfig9,
11475
+ loadConfig as loadConfig10,
11459
11476
  loadMemoriesFromDir as loadMemoriesFromDir33,
11460
11477
  loadUsageIndex as loadUsageIndex26,
11461
11478
  readUsageEvents as readUsageEvents4,
@@ -11470,6 +11487,7 @@ function registerDoctor(program2) {
11470
11487
  const paths = resolveHaivePaths37(root);
11471
11488
  const findings = [];
11472
11489
  const repairs = [];
11490
+ const config = await loadConfig10(paths);
11473
11491
  if (!existsSync63(paths.haiveDir)) {
11474
11492
  findings.push({
11475
11493
  severity: "error",
@@ -11548,7 +11566,7 @@ function registerDoctor(program2) {
11548
11566
  });
11549
11567
  }
11550
11568
  const anchorless = memories.filter(
11551
- (m) => m.memory.frontmatter.anchor.paths.length === 0 && m.memory.frontmatter.anchor.symbols.length === 0 && m.memory.frontmatter.type !== "session_recap" && m.memory.frontmatter.type !== "glossary"
11569
+ (m) => m.memory.frontmatter.anchor.paths.length === 0 && m.memory.frontmatter.anchor.symbols.length === 0 && m.memory.frontmatter.type !== "session_recap" && m.memory.frontmatter.type !== "glossary" && m.memory.frontmatter.type !== "skill"
11552
11570
  );
11553
11571
  if (anchorless.length / Math.max(memories.length, 1) > 0.3) {
11554
11572
  findings.push({
@@ -11573,6 +11591,18 @@ function registerDoctor(program2) {
11573
11591
  });
11574
11592
  }
11575
11593
  }
11594
+ const lintReport = await lintMemoriesAsync(root);
11595
+ if (lintReport.findings.length > 0) {
11596
+ const warnCount = lintReport.findings.filter((finding) => finding.severity === "warn").length;
11597
+ const errorCount = lintReport.findings.filter((finding) => finding.severity === "error").length;
11598
+ const severity = errorCount > 0 ? "error" : warnCount > 0 ? "warn" : "info";
11599
+ findings.push({
11600
+ severity,
11601
+ code: "memory-lint-findings",
11602
+ message: `memory lint reports ${lintReport.findings.length} finding${lintReport.findings.length === 1 ? "" : "s"} (${errorCount} error, ${warnCount} warn, ${lintReport.findings.length - errorCount - warnCount} info).`,
11603
+ fix: "haive memory lint --fix --apply"
11604
+ });
11605
+ }
11576
11606
  const codeMap = await loadCodeMap7(paths);
11577
11607
  if (!codeMap) {
11578
11608
  findings.push({
@@ -11594,6 +11624,8 @@ function registerDoctor(program2) {
11594
11624
  });
11595
11625
  }
11596
11626
  }
11627
+ findings.push(...await collectHarnessCoverageFindings(codeMap, memories));
11628
+ findings.push(...await collectSemanticIndexFindings(paths, config, memories.length, codeMap));
11597
11629
  const events = await readUsageEvents4(paths);
11598
11630
  if (events.length === 0) {
11599
11631
  findings.push({
@@ -11607,6 +11639,7 @@ function registerDoctor(program2) {
11607
11639
  if (!isSearchTool(e.tool)) continue;
11608
11640
  const key = (e.summary ?? "").toLowerCase().trim();
11609
11641
  if (!key) continue;
11642
+ if (isSyntheticSuggestionQuery(key)) continue;
11610
11643
  queryRepeats.set(key, (queryRepeats.get(key) ?? 0) + 1);
11611
11644
  }
11612
11645
  const repeated = [...queryRepeats.entries()].filter(([, n]) => n >= 3);
@@ -11628,7 +11661,6 @@ function registerDoctor(program2) {
11628
11661
  });
11629
11662
  }
11630
11663
  }
11631
- const config = await loadConfig9(paths);
11632
11664
  if (config.enforcement?.requireBriefingFirst) {
11633
11665
  const claudeSettings = path44.join(root, ".claude", "settings.local.json");
11634
11666
  let hasClaudeEnforcement = false;
@@ -11658,14 +11690,14 @@ function registerDoctor(program2) {
11658
11690
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
11659
11691
  });
11660
11692
  }
11661
- findings.push(...await collectInstallFindings(root, "0.9.18"));
11693
+ findings.push(...await collectInstallFindings(root, "0.9.20"));
11662
11694
  try {
11663
11695
  const legacyRaw = execSync3("haive-mcp --version", {
11664
11696
  encoding: "utf8",
11665
11697
  timeout: 3e3,
11666
11698
  stdio: ["ignore", "pipe", "ignore"]
11667
11699
  }).trim();
11668
- const cliVersion = "0.9.18";
11700
+ const cliVersion = "0.9.20";
11669
11701
  if (legacyRaw && legacyRaw !== cliVersion) {
11670
11702
  findings.push({
11671
11703
  severity: "warn",
@@ -11713,7 +11745,7 @@ function emit(findings, opts, repairs = []) {
11713
11745
  console.log(ui.bold(`hAIve doctor \u2014 ${classified.length} finding${classified.length === 1 ? "" : "s"}`));
11714
11746
  console.log(
11715
11747
  ui.dim(
11716
- ` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score}`
11748
+ ` protection=${scores.protection_score} context=${scores.context_quality_score} corpus=${scores.corpus_quality_score} harness-coverage=${scores.harness_coverage_score}%`
11717
11749
  )
11718
11750
  );
11719
11751
  console.log();
@@ -11722,6 +11754,7 @@ function emit(findings, opts, repairs = []) {
11722
11754
  "Agent coverage",
11723
11755
  "Context quality",
11724
11756
  "Corpus health",
11757
+ "Harness coverage",
11725
11758
  "Index health",
11726
11759
  "Next actions"
11727
11760
  ];
@@ -11759,6 +11792,7 @@ function emit(findings, opts, repairs = []) {
11759
11792
  function sectionForFinding(finding) {
11760
11793
  if (finding.code.includes("haive") || finding.code.includes("integration") || finding.code.includes("claude") || finding.code.includes("autopilot")) return "Agent coverage";
11761
11794
  if (finding.code.includes("context") || finding.code.includes("briefing") || finding.code.includes("search")) return "Context quality";
11795
+ if (finding.code.includes("harness-coverage")) return "Harness coverage";
11762
11796
  if (finding.code.includes("code-map") || finding.code.includes("index")) return "Index health";
11763
11797
  if (finding.code.includes("memory") || finding.code.includes("anchor") || finding.code.includes("pending") || finding.code.includes("decay")) return "Corpus health";
11764
11798
  if (finding.severity === "error") return "Protection";
@@ -11774,10 +11808,13 @@ function computeDoctorScores(findings) {
11774
11808
  }, 0);
11775
11809
  return Math.max(0, 100 - penalty);
11776
11810
  };
11811
+ const coverageFinding = findings.find((f) => f.code === "harness-coverage");
11812
+ const harnessCoverageScore = coverageFinding ? parseInt((coverageFinding.message.match(/\((\d+)%\)/) ?? [])[1] ?? "0", 10) : 0;
11777
11813
  return {
11778
11814
  protection_score: scoreFor(["Protection", "Agent coverage"]),
11779
11815
  context_quality_score: scoreFor(["Context quality", "Index health"]),
11780
- corpus_quality_score: scoreFor(["Corpus health"])
11816
+ corpus_quality_score: scoreFor(["Corpus health"]),
11817
+ harness_coverage_score: harnessCoverageScore
11781
11818
  };
11782
11819
  }
11783
11820
  function groupBySection(findings) {
@@ -11786,6 +11823,7 @@ function groupBySection(findings) {
11786
11823
  "Agent coverage": [],
11787
11824
  "Context quality": [],
11788
11825
  "Corpus health": [],
11826
+ "Harness coverage": [],
11789
11827
  "Index health": [],
11790
11828
  "Next actions": []
11791
11829
  };
@@ -11795,6 +11833,87 @@ function groupBySection(findings) {
11795
11833
  function nextActions(findings) {
11796
11834
  return [...new Set(findings.flatMap((finding) => finding.fix ? finding.fix.split("\n") : []))].filter(Boolean);
11797
11835
  }
11836
+ async function collectHarnessCoverageFindings(codeMap, memories) {
11837
+ if (!codeMap) return [];
11838
+ const codeFiles = Object.keys(codeMap.files);
11839
+ const total = codeFiles.length;
11840
+ if (total === 0) return [];
11841
+ const validatedWithAnchors = memories.filter(
11842
+ (m) => (m.memory.frontmatter.status === "validated" || m.memory.frontmatter.status === "proposed") && m.memory.frontmatter.anchor.paths.length > 0
11843
+ );
11844
+ const coveredFiles = /* @__PURE__ */ new Set();
11845
+ for (const m of validatedWithAnchors) {
11846
+ for (const anchorPath of m.memory.frontmatter.anchor.paths) {
11847
+ const normalized = anchorPath.replace(/^\/+/, "");
11848
+ for (const codeFile of codeFiles) {
11849
+ if (codeFile === normalized || codeFile.startsWith(normalized + "/") || normalized.startsWith(codeFile + "/")) {
11850
+ coveredFiles.add(codeFile);
11851
+ }
11852
+ }
11853
+ }
11854
+ }
11855
+ const covered = coveredFiles.size;
11856
+ const pct = Math.round(covered / total * 100);
11857
+ const findings = [];
11858
+ findings.push({
11859
+ severity: pct < 10 && total > 10 ? "info" : "info",
11860
+ code: "harness-coverage",
11861
+ message: `${covered}/${total} code-map files have validated memory anchors (${pct}%). ` + (pct < 10 && total > 10 ? "Low coverage \u2014 add memory anchors on key modules to improve harness enforcement." : pct < 30 ? "Partial coverage \u2014 consider anchoring critical modules and patterns." : "Good harness coverage."),
11862
+ fix: pct < 10 && total > 10 ? "haive memory add --type gotcha|convention|architecture --paths <key-file> --scope team" : void 0,
11863
+ section: "Harness coverage"
11864
+ });
11865
+ return findings;
11866
+ }
11867
+ async function collectSemanticIndexFindings(paths, config, memoryCount, codeMap) {
11868
+ const findings = [];
11869
+ const autoWantsCodeSearch = Boolean(config.autopilot || config.autoRepair?.codeSearch);
11870
+ let mod;
11871
+ try {
11872
+ mod = await import("@hiveai/embeddings");
11873
+ } catch {
11874
+ findings.push({
11875
+ severity: autoWantsCodeSearch ? "warn" : "info",
11876
+ code: "embeddings-unavailable",
11877
+ message: "@hiveai/embeddings is not available, so get_briefing falls back to lexical ranking and code_search cannot run.",
11878
+ fix: "npm install -g @hiveai/cli@latest\nhaive embeddings status",
11879
+ section: "Index health"
11880
+ });
11881
+ return findings;
11882
+ }
11883
+ if (memoryCount > 0) {
11884
+ const stat2 = await mod.indexStat(paths).catch(() => ({ exists: false, count: 0 }));
11885
+ if (!stat2.exists || stat2.count === 0) {
11886
+ findings.push({
11887
+ severity: "warn",
11888
+ code: "semantic-memory-index-missing",
11889
+ message: "Memory embeddings index is missing or empty; get_briefing will report literal_fallback instead of semantic ranking.",
11890
+ fix: "haive embeddings index",
11891
+ section: "Index health"
11892
+ });
11893
+ }
11894
+ }
11895
+ if (autoWantsCodeSearch || codeMap) {
11896
+ const codeIndex = await mod.loadCodeIndex(paths).catch(() => null);
11897
+ if (!codeIndex || codeIndex.entries.length === 0) {
11898
+ findings.push({
11899
+ severity: autoWantsCodeSearch ? "warn" : "info",
11900
+ code: "code-search-index-missing",
11901
+ message: "Code-search embeddings index is missing or empty; MCP code_search is unavailable until it is built.",
11902
+ fix: "haive index code-search",
11903
+ section: "Index health"
11904
+ });
11905
+ } else if (codeMap && codeIndex.source_generated_at !== codeMap.generated_at) {
11906
+ findings.push({
11907
+ severity: "info",
11908
+ code: "code-search-index-outdated",
11909
+ message: "Code-search embeddings index was built from an older code-map; semantic code search may miss recent symbols.",
11910
+ fix: "haive index code-search",
11911
+ section: "Index health"
11912
+ });
11913
+ }
11914
+ }
11915
+ return findings;
11916
+ }
11798
11917
  function isSearchTool(name) {
11799
11918
  return ["mem_search", "code_search", "mem_relevant_to", "get_briefing"].includes(name);
11800
11919
  }
@@ -12149,12 +12268,13 @@ import {
12149
12268
  resolveHaivePaths as resolveHaivePaths40
12150
12269
  } from "@hiveai/core";
12151
12270
  var TYPE_RANK = {
12152
- decision: 0,
12153
- architecture: 1,
12154
- convention: 2,
12155
- glossary: 3,
12156
- gotcha: 4,
12157
- attempt: 5
12271
+ skill: 0,
12272
+ decision: 1,
12273
+ architecture: 2,
12274
+ convention: 3,
12275
+ glossary: 4,
12276
+ gotcha: 5,
12277
+ attempt: 6
12158
12278
  };
12159
12279
  function registerWelcome(program2) {
12160
12280
  program2.command("welcome").description(
@@ -12381,7 +12501,7 @@ import {
12381
12501
  findProjectRoot as findProjectRoot48,
12382
12502
  hasRecentBriefingMarker,
12383
12503
  isFreshIsoDate,
12384
- loadConfig as loadConfig10,
12504
+ loadConfig as loadConfig11,
12385
12505
  loadMemoriesFromDir as loadMemoriesFromDir36,
12386
12506
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
12387
12507
  readRecentBriefingMarker,
@@ -12402,7 +12522,7 @@ function registerEnforce(program2) {
12402
12522
  const root = findProjectRoot48(opts.dir);
12403
12523
  const paths = resolveHaivePaths44(root);
12404
12524
  await mkdir19(paths.haiveDir, { recursive: true });
12405
- const current = await loadConfig10(paths);
12525
+ const current = await loadConfig11(paths);
12406
12526
  await saveConfig4(paths, {
12407
12527
  ...current,
12408
12528
  enforcement: {
@@ -12651,7 +12771,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
12651
12771
  const root = findProjectRoot48(dir);
12652
12772
  const paths = resolveHaivePaths44(root);
12653
12773
  const initialized = existsSync69(paths.haiveDir);
12654
- const config = initialized ? await loadConfig10(paths) : {};
12774
+ const config = initialized ? await loadConfig11(paths) : {};
12655
12775
  const mode = config.enforcement?.mode ?? "strict";
12656
12776
  const findings = [];
12657
12777
  if (!initialized) {
@@ -12680,7 +12800,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
12680
12800
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
12681
12801
  });
12682
12802
  }
12683
- findings.push(...await inspectIntegrationVersions(root, "0.9.18"));
12803
+ findings.push(...await inspectIntegrationVersions(root, "0.9.20"));
12684
12804
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
12685
12805
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
12686
12806
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -13168,7 +13288,7 @@ function registerRun(program2) {
13168
13288
 
13169
13289
  // src/index.ts
13170
13290
  var program = new Command51();
13171
- program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.18").option("--advanced", "show maintenance and experimental commands in help");
13291
+ program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.20").option("--advanced", "show maintenance and experimental commands in help");
13172
13292
  registerInit(program);
13173
13293
  registerWelcome(program);
13174
13294
  registerResolveProject(program);
@@ -13265,7 +13385,7 @@ function applySurfaceVisibility(root) {
13265
13385
  "after",
13266
13386
  [
13267
13387
  "",
13268
- "Default help shows the core hAIve harness: init, doctor, agent setup, briefing, enforcement,",
13388
+ "Default help shows the core harness workflow: init, doctor, agent setup, briefing, enforcement,",
13269
13389
  "sync, session recaps, and high-signal memory commands.",
13270
13390
  "Run `haive --advanced --help` or set HAIVE_SHOW_ADVANCED=1 to show maintenance and experimental commands."
13271
13391
  ].join("\n")