@hiveai/cli 0.9.27 → 0.9.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command51 } from "commander";
4
+ import { Command as Command52 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync as existsSync3 } from "fs";
@@ -11,6 +11,7 @@ import "commander";
11
11
  import {
12
12
  extractActionsBriefBody,
13
13
  findProjectRoot as findProjectRoot2,
14
+ isStackPackSeed,
14
15
  literalMatchesAllTokens,
15
16
  literalMatchesAnyToken,
16
17
  loadCodeMap as loadCodeMap3,
@@ -198,7 +199,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
198
199
  if (!f) continue;
199
200
  counts.set(f, (counts.get(f) ?? 0) + 1);
200
201
  }
201
- let entries = [...counts.entries()].map(([path50, changes]) => ({ path: path50, changes }));
202
+ let entries = [...counts.entries()].map(([path51, changes]) => ({ path: path51, changes }));
202
203
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
203
204
  if (lowerPaths.length > 0) {
204
205
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -1098,6 +1099,7 @@ function classifyCliPriority(item, filePaths, tokens, exactTaskHit, partialTaskH
1098
1099
  const fm = item.memory.frontmatter;
1099
1100
  const anchored = filePaths.length > 0 && memoryMatchesAnchorPaths(item.memory, filePaths);
1100
1101
  if (anchored || fm.type === "attempt" && exactTaskHit) return "must_read";
1102
+ if (isStackPackSeed(fm)) return "background";
1101
1103
  if (exactTaskHit || partialTaskHit || item.score >= 4 || tokens && fm.tags.some((tag) => tokens.includes(tag))) {
1102
1104
  return "useful";
1103
1105
  }
@@ -2018,7 +2020,8 @@ import path9 from "path";
2018
2020
  import {
2019
2021
  buildFrontmatter,
2020
2022
  memoryFilePath,
2021
- serializeMemory as serializeMemory2
2023
+ serializeMemory as serializeMemory2,
2024
+ STACK_PACK_TAG
2022
2025
  } from "@hiveai/core";
2023
2026
  var PACKS = {
2024
2027
  nestjs: [
@@ -2593,6 +2596,7 @@ new ApolloServer({
2593
2596
  }
2594
2597
  ]
2595
2598
  };
2599
+ var SEED_FOOTER = (stack) => `> _Seeded by \`haive init\` from the **${stack}** stack pack \u2014 generic guidance, not repo-specific. Anchor it to a real file or replace it with a repo-specific note to raise it above background priority._`;
2596
2600
  var SUPPORTED_STACKS = Object.keys(PACKS);
2597
2601
  function isValidStack(name) {
2598
2602
  return name in PACKS;
@@ -2634,11 +2638,15 @@ async function seedStackPack(haivePaths, stack) {
2634
2638
  slug: `${stack}-${mem.slug}`,
2635
2639
  scope: "team",
2636
2640
  status: "validated",
2637
- tags: mem.tags
2641
+ // STACK_PACK_TAG marks this as generic seed knowledge so briefing ranking
2642
+ // keeps it at `background` priority until it earns a repo-specific anchor.
2643
+ tags: [...mem.tags, STACK_PACK_TAG]
2638
2644
  });
2639
2645
  const filePath = memoryFilePath(haivePaths, "team", fm.id);
2640
2646
  if (existsSync8(filePath)) continue;
2641
- const content = serializeMemory2({ frontmatter: fm, body: mem.body });
2647
+ const content = serializeMemory2({ frontmatter: fm, body: `${mem.body}
2648
+
2649
+ ${SEED_FOOTER(stack)}` });
2642
2650
  await mkdir4(path9.dirname(filePath), { recursive: true });
2643
2651
  await writeFile5(filePath, content, "utf8");
2644
2652
  count++;
@@ -2669,30 +2677,25 @@ TODO \u2014 domain terms and what they mean here.
2669
2677
  TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.
2670
2678
  `;
2671
2679
  var BRIDGE_BODY = `<!-- hAIve bridge file \u2014 do not edit by hand. -->
2672
- <!-- This file points your AI tool at the shared hAIve project context. -->
2673
-
2674
- See \`.ai/project-context.md\` for the full project context.
2675
- Memories live under \`.ai/memories/\` (personal/team/module).
2676
2680
 
2677
- ## hAIve \u2014 mandatory rules for AI agents
2681
+ This repo uses **hAIve** for shared context. The map:
2678
2682
 
2679
- Installing or initializing hAIve in this repository means **the team expects AI agents to drive work through hAIve**, not bypass it.
2683
+ - \`.ai/project-context.md\` \u2014 project overview, architecture, conventions.
2684
+ - \`.ai/memories/\` \u2014 decisions, gotchas, conventions, failed attempts (personal/team/module).
2685
+ - The breadcrumbs injected below (if any) are the top current memories.
2680
2686
 
2681
- ### MCP first (default \u2014 never optional unless the developer says so)
2682
- The **haive** MCP server must be enabled in your client. Until it is unavailable (misconfiguration report to the developer), you **must** use it for substantive work:
2687
+ ## Working through hAIve
2683
2688
 
2684
- - **Before** you edit project source files, configs (\`package.json\`, tooling), or workflows for the current goal, invoke \`get_briefing\` with a short **task** string (and **files** / **symbols** when you know them). If project context was already refreshed in this session and you only need ranked memories, you may call \`mem_relevant_to\` instead\u2014but **still** consult hAIve before trusting ungrounded guesses.
2685
- - **Before** declaring a coding task finished, invoke the MCP prompt \`post_task\` when the session produced learnings or substantive changes worth capturing.
2689
+ 1. **Before editing** for a goal, call \`get_briefing\` (task + files/symbols) to load ranked context \u2014 or \`mem_relevant_to\` if project context is already loaded this session.
2690
+ 2. **When an approach fails**, call \`mem_tried\` right away so the next agent skips the dead end.
2691
+ 3. **Before closing** a substantive session, run the \`post_task\` prompt to capture what was learned.
2686
2692
 
2687
- Do **not** treat these as nice-to-have: they are **default-required** for coding agents here.
2693
+ If the haive MCP server is not available, tell the developer rather than silently skipping it.
2688
2694
 
2689
- ### Session rules
2690
- - **Immediately** when an approach fails (wrong API, wrong pattern, wrong assumption), call \`mem_tried\` \u2014 do not wait until the end of the session.
2695
+ ## Safety
2691
2696
 
2692
- ### Safety rules \u2014 NEVER violate these
2693
- - If \`get_briefing\` returns an \`action_required\` list, **stop and show each item to the developer** before doing anything. Use the exact \`developer_message\` provided. Wait for explicit confirmation.
2694
- - **Never modify code autonomously** because of a breaking change detected in another project (dependency version bump, API contract change, removed field). Always ask first.
2695
- - When in doubt about a cross-repo change: ask, don't act.
2697
+ - If \`get_briefing\` returns \`action_required\`, surface each item to the developer (use its \`developer_message\`) and wait for confirmation before changing code.
2698
+ - Never act autonomously on a cross-repo breaking change (dep bump, contract/API diff) \u2014 ask first.
2696
2699
  `;
2697
2700
  var CURSOR_HAIVE_RULE_MDC = `---
2698
2701
  description: Require hAIve MCP (get_briefing / mem_relevant_to) before substantive repo edits
@@ -2918,7 +2921,10 @@ function registerInit(program2) {
2918
2921
  }
2919
2922
  if (totalSeeded > 0) {
2920
2923
  ui.success(
2921
- `${totalSeeded} validated team memories pre-seeded \u2014 haive is useful from J+0`
2924
+ `${totalSeeded} starter memories seeded (generic stack guidance, kept at background priority)`
2925
+ );
2926
+ ui.info(
2927
+ "Anchor them to real files or replace them with repo-specific notes to make them high-signal."
2922
2928
  );
2923
2929
  }
2924
2930
  }
@@ -3663,6 +3669,7 @@ import {
3663
3669
  isGlobPath,
3664
3670
  isAutoPromoteEligible,
3665
3671
  isDecaying,
3672
+ isStackPackSeed as isStackPackSeed2,
3666
3673
  literalMatchesAllTokens as literalMatchesAllTokens22,
3667
3674
  literalMatchesAnyToken as literalMatchesAnyToken22,
3668
3675
  loadCodeMap as loadCodeMap4,
@@ -3676,7 +3683,8 @@ import {
3676
3683
  serializeMemory as serializeMemory9,
3677
3684
  tokenizeQuery as tokenizeQuery22,
3678
3685
  trackReads as trackReads3,
3679
- truncateToTokens
3686
+ truncateToTokens,
3687
+ writeBriefingMarker as writeBriefingMarker2
3680
3688
  } from "@hiveai/core";
3681
3689
  import { z as z17 } from "zod";
3682
3690
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
@@ -5427,6 +5435,16 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
5427
5435
  );
5428
5436
  }
5429
5437
  }
5438
+ if (existsSync18(ctx.paths.haiveDir)) {
5439
+ await writeBriefingMarker2(ctx.paths, {
5440
+ sessionId: process.env.HAIVE_SESSION_ID,
5441
+ ...input.task ? { task: input.task } : {},
5442
+ source: "mcp-get-briefing",
5443
+ files: input.files,
5444
+ memoryIds: outputMemories.map((m) => m.id)
5445
+ }).catch(() => {
5446
+ });
5447
+ }
5430
5448
  return {
5431
5449
  ...input.task ? { task: input.task } : {},
5432
5450
  search_mode: searchMode,
@@ -5481,6 +5499,9 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
5481
5499
  if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
5482
5500
  return "must_read";
5483
5501
  }
5502
+ if (isStackPackSeed2(fm)) {
5503
+ return "background";
5504
+ }
5484
5505
  if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
5485
5506
  return "useful";
5486
5507
  }
@@ -5963,6 +5984,8 @@ async function antiPatternsCheck(input, ctx) {
5963
5984
  confidence: deriveConfidence6(fm, u),
5964
5985
  body_preview: body.split("\n").slice(0, 5).join("\n").slice(0, 400),
5965
5986
  reasons: [reason],
5987
+ tags: fm.tags ?? [],
5988
+ anchor_paths: fm.anchor?.paths ?? [],
5966
5989
  ...score !== void 0 ? { semantic_score: score } : {}
5967
5990
  });
5968
5991
  };
@@ -6553,8 +6576,45 @@ function fileTypeDowngradeReason(warning, paths) {
6553
6576
  if (configOnly && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
6554
6577
  return "package/config-only change; warning has no anchor on these files and no strong semantic match \u2014 downgraded to info.";
6555
6578
  }
6579
+ const touchesBuildFile = paths.some(isPackageOrConfigPath);
6580
+ if (!touchesBuildFile && isBuildScopedWarning(warning) && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
6581
+ return "build/packaging gotcha, but no package/build file changed \u2014 downgraded to info.";
6582
+ }
6556
6583
  return null;
6557
6584
  }
6585
+ function isBuildScopedWarning(warning) {
6586
+ const tags = warning.tags ?? [];
6587
+ if (tags.some((t) => BUILD_SCOPED_TAGS.has(t.toLowerCase()))) return true;
6588
+ const anchors = warning.anchor_paths ?? [];
6589
+ return anchors.length > 0 && anchors.every(isPackageOrConfigPath);
6590
+ }
6591
+ var BUILD_SCOPED_TAGS = /* @__PURE__ */ new Set([
6592
+ "npm",
6593
+ "pnpm",
6594
+ "yarn",
6595
+ "publish",
6596
+ "install",
6597
+ "packaging",
6598
+ "package",
6599
+ "build",
6600
+ "tsup",
6601
+ "bundler",
6602
+ "monorepo",
6603
+ "workspace",
6604
+ "versioning",
6605
+ "version",
6606
+ "dev-workflow",
6607
+ "hotswap",
6608
+ "ci",
6609
+ "workflow",
6610
+ "release",
6611
+ "changelog",
6612
+ "dependencies",
6613
+ "deps",
6614
+ "dependency",
6615
+ "tooling",
6616
+ "config"
6617
+ ]);
6558
6618
  function hasStrongSemantic(warning) {
6559
6619
  return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
6560
6620
  }
@@ -7130,7 +7190,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7130
7190
  };
7131
7191
  }
7132
7192
  var SERVER_NAME = "haive";
7133
- var SERVER_VERSION = "0.9.27";
7193
+ var SERVER_VERSION = "0.9.29";
7134
7194
  function jsonResult(data) {
7135
7195
  return {
7136
7196
  content: [
@@ -8111,6 +8171,7 @@ import {
8111
8171
  getUsage as getUsage10,
8112
8172
  isAutoPromoteEligible as isAutoPromoteEligible2,
8113
8173
  isDecaying as isDecaying2,
8174
+ isStackPackSeed as isStackPackSeed3,
8114
8175
  loadCodeMap as loadCodeMap5,
8115
8176
  loadConfig as loadConfig4,
8116
8177
  loadMemoriesFromDir as loadMemoriesFromDir23,
@@ -8555,12 +8616,18 @@ Attends une **confirmation explicite** avant d'agir.
8555
8616
  }
8556
8617
  });
8557
8618
  }
8619
+ function bridgeSummaryLine(body) {
8620
+ const firstLine = body.split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l.length > 0) ?? "";
8621
+ const oneLine = firstLine.replace(/\s+/g, " ");
8622
+ return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
8623
+ }
8558
8624
  async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8559
8625
  if (!existsSync29(memoriesDir)) return;
8560
8626
  const all = await loadMemoriesFromDir23(memoriesDir);
8561
8627
  const top = all.filter(({ memory: memory2 }) => {
8562
8628
  const s = memory2.frontmatter.status;
8563
8629
  if (memory2.frontmatter.type === "session_recap") return false;
8630
+ if (isStackPackSeed3(memory2.frontmatter)) return false;
8564
8631
  return s === "validated" || s === "proposed";
8565
8632
  }).sort((a, b) => {
8566
8633
  const score = (m) => {
@@ -8572,11 +8639,11 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
8572
8639
  const block = top.map((m) => {
8573
8640
  const fm = m.memory.frontmatter;
8574
8641
  const unverified = fm.status === "proposed" ? " [UNVERIFIED]" : "";
8575
- return `### ${fm.id} (${fm.scope}/${fm.type})${unverified}
8576
- ${m.memory.body.trim()}`;
8577
- }).join("\n\n---\n\n");
8642
+ return `- \`${fm.id}\` (${fm.scope}/${fm.type})${unverified} \u2014 ${bridgeSummaryLine(m.memory.body)}`;
8643
+ }).join("\n");
8578
8644
  const injected = `${BRIDGE_START}
8579
8645
  <!-- AUTO-GENERATED by haive sync --inject-bridge \u2014 do not edit between these markers -->
8646
+ <!-- Top memories \u2014 call get_briefing / mem_get for the full body. -->
8580
8647
 
8581
8648
  ` + block + `
8582
8649
 
@@ -9545,21 +9612,118 @@ function parseCsv4(value) {
9545
9612
  return value.split(",").map((s) => s.trim()).filter(Boolean);
9546
9613
  }
9547
9614
 
9548
- // src/commands/memory-pending.ts
9615
+ // src/commands/memory-seed.ts
9616
+ import { readFile as readFile13 } from "fs/promises";
9549
9617
  import { existsSync as existsSync41 } from "fs";
9550
9618
  import path27 from "path";
9551
9619
  import "commander";
9552
9620
  import {
9553
9621
  findProjectRoot as findProjectRoot23,
9622
+ loadConfig as loadConfig6,
9623
+ resolveHaivePaths as resolveHaivePaths20
9624
+ } from "@hiveai/core";
9625
+ async function readDependencyMap(root) {
9626
+ try {
9627
+ const raw = await readFile13(path27.join(root, "package.json"), "utf8");
9628
+ const pkg = JSON.parse(raw);
9629
+ return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
9630
+ } catch {
9631
+ return {};
9632
+ }
9633
+ }
9634
+ function registerMemorySeed(memory2) {
9635
+ memory2.command("seed [stack]").description(
9636
+ "Seed a stack pack of starter memories on demand.\n\n Stack packs are generic framework gotchas/conventions every team using that\n stack rediscovers. They are tagged `stack-pack` and kept at BACKGROUND priority\n in briefings until you anchor them to a real file or replace them with a\n repo-specific note \u2014 so they never crowd out your own knowledge.\n\n Examples:\n haive memory seed # auto-detect stacks from package.json and seed them\n haive memory seed nestjs # seed a specific stack\n haive memory seed --list # show supported + auto-detected stacks\n haive memory seed --list --json\n"
9637
+ ).option("--list", "list supported stacks (and which are auto-detected here) and exit").option("--json", "machine-readable output (use with --list)").option("-d, --dir <dir>", "project root").action(async (stack, opts) => {
9638
+ const root = findProjectRoot23(opts.dir);
9639
+ const paths = resolveHaivePaths20(root);
9640
+ const deps = await readDependencyMap(root);
9641
+ const detected = autoDetectStacks(deps);
9642
+ if (opts.list) {
9643
+ if (opts.json) {
9644
+ console.log(JSON.stringify({ supported: SUPPORTED_STACKS, detected }, null, 2));
9645
+ return;
9646
+ }
9647
+ ui.info("Supported stacks:");
9648
+ for (const s of SUPPORTED_STACKS) {
9649
+ const mark = detected.includes(s) ? ui.green(" \u2713 detected here") : "";
9650
+ console.log(` \u2022 ${s}${mark}`);
9651
+ }
9652
+ if (detected.length === 0) {
9653
+ ui.info("No stack auto-detected from package.json \u2014 pass a stack name explicitly.");
9654
+ }
9655
+ return;
9656
+ }
9657
+ if (!existsSync41(paths.haiveDir)) {
9658
+ ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
9659
+ process.exitCode = 1;
9660
+ return;
9661
+ }
9662
+ let stacksToSeed;
9663
+ if (stack) {
9664
+ if (!isValidStack(stack)) {
9665
+ ui.error(`Unknown stack '${stack}'. Supported: ${SUPPORTED_STACKS.join(", ")}.`);
9666
+ ui.info("Run `haive memory seed --list` to see all stacks.");
9667
+ process.exitCode = 1;
9668
+ return;
9669
+ }
9670
+ stacksToSeed = [stack];
9671
+ } else if (detected.length > 0) {
9672
+ stacksToSeed = detected;
9673
+ ui.info(`Auto-detected from package.json: ${detected.join(", ")}`);
9674
+ } else {
9675
+ ui.error("No stack auto-detected from package.json.");
9676
+ ui.info("Pass a stack name (e.g. `haive memory seed nestjs`) or run `haive memory seed --list`.");
9677
+ process.exitCode = 1;
9678
+ return;
9679
+ }
9680
+ let total = 0;
9681
+ const seededStacks = [];
9682
+ for (const s of stacksToSeed) {
9683
+ const count = await seedStackPack(paths, s);
9684
+ if (count > 0) {
9685
+ total += count;
9686
+ seededStacks.push(`${s} (${count})`);
9687
+ } else {
9688
+ ui.info(`Stack pack '${s}': all memories already exist \u2014 skipped.`);
9689
+ }
9690
+ }
9691
+ if (total === 0) {
9692
+ ui.info("Nothing new to seed \u2014 every memory in the selected pack(s) already exists.");
9693
+ return;
9694
+ }
9695
+ ui.success(`Seeded ${total} starter memor${total === 1 ? "y" : "ies"}: ${seededStacks.join(", ")}`);
9696
+ ui.info("Kept at background priority. Anchor them to a real file (or replace them) to make them high-signal:");
9697
+ ui.info(" haive memory update <id> --paths <key-file> # anchor a seed to a file");
9698
+ const config = await loadConfig6(paths);
9699
+ if (config.autopilot || config.autoRepair?.corpus === true) {
9700
+ const repairs = await applyAutopilotRepairs(root, paths, {
9701
+ applyConfig: false,
9702
+ applyContext: false,
9703
+ applyCorpus: true,
9704
+ applyCodeMap: false,
9705
+ applyCodeSearch: false
9706
+ });
9707
+ for (const repair of repairs) ui.info(repair.message);
9708
+ }
9709
+ });
9710
+ }
9711
+
9712
+ // src/commands/memory-pending.ts
9713
+ import { existsSync as existsSync43 } from "fs";
9714
+ import path28 from "path";
9715
+ import "commander";
9716
+ import {
9717
+ findProjectRoot as findProjectRoot24,
9554
9718
  getUsage as getUsage14,
9555
9719
  loadUsageIndex as loadUsageIndex16,
9556
- resolveHaivePaths as resolveHaivePaths20
9720
+ resolveHaivePaths as resolveHaivePaths21
9557
9721
  } from "@hiveai/core";
9558
9722
  function registerMemoryPending(memory2) {
9559
9723
  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) => {
9560
- const root = findProjectRoot23(opts.dir);
9561
- const paths = resolveHaivePaths20(root);
9562
- if (!existsSync41(paths.memoriesDir)) {
9724
+ const root = findProjectRoot24(opts.dir);
9725
+ const paths = resolveHaivePaths21(root);
9726
+ if (!existsSync43(paths.memoriesDir)) {
9563
9727
  ui.error(`No .ai/memories at ${root}.`);
9564
9728
  process.exitCode = 1;
9565
9729
  return;
@@ -9593,7 +9757,7 @@ function registerMemoryPending(memory2) {
9593
9757
  console.log(
9594
9758
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
9595
9759
  );
9596
- console.log(` ${ui.dim(path27.relative(root, filePath))}`);
9760
+ console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9597
9761
  }
9598
9762
  if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
9599
9763
  console.log();
@@ -9608,7 +9772,7 @@ function registerMemoryPending(memory2) {
9608
9772
  console.log(
9609
9773
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
9610
9774
  );
9611
- console.log(` ${ui.dim(path27.relative(root, filePath))}`);
9775
+ console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9612
9776
  }
9613
9777
  console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
9614
9778
  }
@@ -9617,24 +9781,24 @@ function registerMemoryPending(memory2) {
9617
9781
  }
9618
9782
 
9619
9783
  // src/commands/memory-query.ts
9620
- import { existsSync as existsSync43 } from "fs";
9621
- import path28 from "path";
9784
+ import { existsSync as existsSync44 } from "fs";
9785
+ import path29 from "path";
9622
9786
  import "commander";
9623
9787
  import {
9624
9788
  extractSnippet as extractSnippet2,
9625
- findProjectRoot as findProjectRoot24,
9789
+ findProjectRoot as findProjectRoot25,
9626
9790
  literalMatchesAllTokens as literalMatchesAllTokens3,
9627
9791
  literalMatchesAnyToken as literalMatchesAnyToken4,
9628
9792
  pickSnippetNeedle as pickSnippetNeedle2,
9629
- resolveHaivePaths as resolveHaivePaths21,
9793
+ resolveHaivePaths as resolveHaivePaths22,
9630
9794
  tokenizeQuery as tokenizeQuery6,
9631
9795
  trackReads as trackReads4
9632
9796
  } from "@hiveai/core";
9633
9797
  function registerMemoryQuery(memory2) {
9634
9798
  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) => {
9635
- const root = findProjectRoot24(opts.dir);
9636
- const paths = resolveHaivePaths21(root);
9637
- if (!existsSync43(paths.memoriesDir)) {
9799
+ const root = findProjectRoot25(opts.dir);
9800
+ const paths = resolveHaivePaths22(root);
9801
+ if (!existsSync44(paths.memoriesDir)) {
9638
9802
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9639
9803
  process.exitCode = 1;
9640
9804
  return;
@@ -9675,7 +9839,7 @@ function registerMemoryQuery(memory2) {
9675
9839
  const fm = mem.frontmatter;
9676
9840
  const statusBadge = ui.statusBadge(fm.status);
9677
9841
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
9678
- console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9842
+ console.log(` ${ui.dim(path29.relative(root, filePath))}`);
9679
9843
  const snippet = extractSnippet2(mem.body, snippetNeedle);
9680
9844
  if (snippet) console.log(` ${snippet}`);
9681
9845
  }
@@ -9693,21 +9857,21 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
9693
9857
 
9694
9858
  // src/commands/memory-reject.ts
9695
9859
  import { writeFile as writeFile20 } from "fs/promises";
9696
- import { existsSync as existsSync44 } from "fs";
9860
+ import { existsSync as existsSync45 } from "fs";
9697
9861
  import "commander";
9698
9862
  import {
9699
- findProjectRoot as findProjectRoot25,
9863
+ findProjectRoot as findProjectRoot26,
9700
9864
  loadUsageIndex as loadUsageIndex17,
9701
9865
  recordRejection as recordRejection2,
9702
- resolveHaivePaths as resolveHaivePaths22,
9866
+ resolveHaivePaths as resolveHaivePaths23,
9703
9867
  saveUsageIndex as saveUsageIndex3,
9704
9868
  serializeMemory as serializeMemory18
9705
9869
  } from "@hiveai/core";
9706
9870
  function registerMemoryReject(memory2) {
9707
9871
  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) => {
9708
- const root = findProjectRoot25(opts.dir);
9709
- const paths = resolveHaivePaths22(root);
9710
- if (!existsSync44(paths.memoriesDir)) {
9872
+ const root = findProjectRoot26(opts.dir);
9873
+ const paths = resolveHaivePaths23(root);
9874
+ if (!existsSync45(paths.memoriesDir)) {
9711
9875
  ui.error(`No .ai/memories at ${root}.`);
9712
9876
  process.exitCode = 1;
9713
9877
  return;
@@ -9743,22 +9907,22 @@ function registerMemoryReject(memory2) {
9743
9907
  }
9744
9908
 
9745
9909
  // src/commands/memory-rm.ts
9746
- import { existsSync as existsSync45 } from "fs";
9910
+ import { existsSync as existsSync46 } from "fs";
9747
9911
  import { unlink as unlink3 } from "fs/promises";
9748
- import path29 from "path";
9912
+ import path30 from "path";
9749
9913
  import { createInterface as createInterface2 } from "readline/promises";
9750
9914
  import "commander";
9751
9915
  import {
9752
- findProjectRoot as findProjectRoot26,
9916
+ findProjectRoot as findProjectRoot27,
9753
9917
  loadUsageIndex as loadUsageIndex18,
9754
- resolveHaivePaths as resolveHaivePaths23,
9918
+ resolveHaivePaths as resolveHaivePaths24,
9755
9919
  saveUsageIndex as saveUsageIndex4
9756
9920
  } from "@hiveai/core";
9757
9921
  function registerMemoryRm(memory2) {
9758
9922
  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) => {
9759
- const root = findProjectRoot26(opts.dir);
9760
- const paths = resolveHaivePaths23(root);
9761
- if (!existsSync45(paths.memoriesDir)) {
9923
+ const root = findProjectRoot27(opts.dir);
9924
+ const paths = resolveHaivePaths24(root);
9925
+ if (!existsSync46(paths.memoriesDir)) {
9762
9926
  ui.error(`No .ai/memories at ${root}.`);
9763
9927
  process.exitCode = 1;
9764
9928
  return;
@@ -9770,7 +9934,7 @@ function registerMemoryRm(memory2) {
9770
9934
  process.exitCode = 1;
9771
9935
  return;
9772
9936
  }
9773
- const rel = path29.relative(root, found.filePath);
9937
+ const rel = path30.relative(root, found.filePath);
9774
9938
  if (!opts.yes) {
9775
9939
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
9776
9940
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
@@ -9794,22 +9958,22 @@ function registerMemoryRm(memory2) {
9794
9958
  }
9795
9959
 
9796
9960
  // src/commands/memory-show.ts
9797
- import { existsSync as existsSync46 } from "fs";
9798
- import { readFile as readFile13 } from "fs/promises";
9799
- import path30 from "path";
9961
+ import { existsSync as existsSync47 } from "fs";
9962
+ import { readFile as readFile14 } from "fs/promises";
9963
+ import path31 from "path";
9800
9964
  import "commander";
9801
9965
  import {
9802
9966
  deriveConfidence as deriveConfidence10,
9803
- findProjectRoot as findProjectRoot27,
9967
+ findProjectRoot as findProjectRoot28,
9804
9968
  getUsage as getUsage15,
9805
9969
  loadUsageIndex as loadUsageIndex19,
9806
- resolveHaivePaths as resolveHaivePaths24
9970
+ resolveHaivePaths as resolveHaivePaths25
9807
9971
  } from "@hiveai/core";
9808
9972
  function registerMemoryShow(memory2) {
9809
9973
  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) => {
9810
- const root = findProjectRoot27(opts.dir);
9811
- const paths = resolveHaivePaths24(root);
9812
- if (!existsSync46(paths.memoriesDir)) {
9974
+ const root = findProjectRoot28(opts.dir);
9975
+ const paths = resolveHaivePaths25(root);
9976
+ if (!existsSync47(paths.memoriesDir)) {
9813
9977
  ui.error(`No .ai/memories at ${root}.`);
9814
9978
  process.exitCode = 1;
9815
9979
  return;
@@ -9822,7 +9986,7 @@ function registerMemoryShow(memory2) {
9822
9986
  return;
9823
9987
  }
9824
9988
  if (opts.raw) {
9825
- console.log(await readFile13(found.filePath, "utf8"));
9989
+ console.log(await readFile14(found.filePath, "utf8"));
9826
9990
  return;
9827
9991
  }
9828
9992
  const fm = found.memory.frontmatter;
@@ -9838,7 +10002,7 @@ function registerMemoryShow(memory2) {
9838
10002
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
9839
10003
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
9840
10004
  console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
9841
- console.log(`${ui.dim("file:")} ${path30.relative(root, found.filePath)}`);
10005
+ console.log(`${ui.dim("file:")} ${path31.relative(root, found.filePath)}`);
9842
10006
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
9843
10007
  console.log(ui.dim("anchor:"));
9844
10008
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -9853,21 +10017,21 @@ function registerMemoryShow(memory2) {
9853
10017
  }
9854
10018
 
9855
10019
  // src/commands/memory-stats.ts
9856
- import { existsSync as existsSync47 } from "fs";
9857
- import path31 from "path";
10020
+ import { existsSync as existsSync48 } from "fs";
10021
+ import path33 from "path";
9858
10022
  import "commander";
9859
10023
  import {
9860
10024
  deriveConfidence as deriveConfidence11,
9861
- findProjectRoot as findProjectRoot28,
10025
+ findProjectRoot as findProjectRoot29,
9862
10026
  getUsage as getUsage16,
9863
10027
  loadUsageIndex as loadUsageIndex20,
9864
- resolveHaivePaths as resolveHaivePaths25
10028
+ resolveHaivePaths as resolveHaivePaths26
9865
10029
  } from "@hiveai/core";
9866
10030
  function registerMemoryStats(memory2) {
9867
10031
  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) => {
9868
- const root = findProjectRoot28(opts.dir);
9869
- const paths = resolveHaivePaths25(root);
9870
- if (!existsSync47(paths.memoriesDir)) {
10032
+ const root = findProjectRoot29(opts.dir);
10033
+ const paths = resolveHaivePaths26(root);
10034
+ if (!existsSync48(paths.memoriesDir)) {
9871
10035
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9872
10036
  process.exitCode = 1;
9873
10037
  return;
@@ -9892,19 +10056,19 @@ function registerMemoryStats(memory2) {
9892
10056
  console.log(
9893
10057
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
9894
10058
  );
9895
- console.log(` ${ui.dim(path31.relative(root, filePath))}`);
10059
+ console.log(` ${ui.dim(path33.relative(root, filePath))}`);
9896
10060
  }
9897
10061
  });
9898
10062
  }
9899
10063
 
9900
10064
  // src/commands/memory-verify.ts
9901
10065
  import { writeFile as writeFile21 } from "fs/promises";
9902
- import { existsSync as existsSync48 } from "fs";
9903
- import path33 from "path";
10066
+ import { existsSync as existsSync49 } from "fs";
10067
+ import path34 from "path";
9904
10068
  import "commander";
9905
10069
  import {
9906
- findProjectRoot as findProjectRoot29,
9907
- resolveHaivePaths as resolveHaivePaths26,
10070
+ findProjectRoot as findProjectRoot30,
10071
+ resolveHaivePaths as resolveHaivePaths27,
9908
10072
  serializeMemory as serializeMemory19,
9909
10073
  verifyAnchor as verifyAnchor3
9910
10074
  } from "@hiveai/core";
@@ -9912,9 +10076,9 @@ function registerMemoryVerify(memory2) {
9912
10076
  memory2.command("verify").description(
9913
10077
  "Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n haive sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'haive memory verify' to your haive-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n haive memory verify # check all, report only\n haive memory verify --update # mark stale/fresh on disk\n haive memory verify --id 2026-04-28-gotcha-x # check one memory\n"
9914
10078
  ).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("-d, --dir <dir>", "project root").action(async (opts) => {
9915
- const root = findProjectRoot29(opts.dir);
9916
- const paths = resolveHaivePaths26(root);
9917
- if (!existsSync48(paths.memoriesDir)) {
10079
+ const root = findProjectRoot30(opts.dir);
10080
+ const paths = resolveHaivePaths27(root);
10081
+ if (!existsSync49(paths.memoriesDir)) {
9918
10082
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9919
10083
  process.exitCode = 1;
9920
10084
  return;
@@ -9937,7 +10101,7 @@ function registerMemoryVerify(memory2) {
9937
10101
  anchorlessIds.push(mem.frontmatter.id);
9938
10102
  continue;
9939
10103
  }
9940
- const rel = path33.relative(root, filePath);
10104
+ const rel = path34.relative(root, filePath);
9941
10105
  if (result.stale) {
9942
10106
  staleCount++;
9943
10107
  console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
@@ -10000,30 +10164,30 @@ function applyVerification2(mem, result) {
10000
10164
  }
10001
10165
 
10002
10166
  // src/commands/memory-import.ts
10003
- import { readFile as readFile14 } from "fs/promises";
10004
- import { existsSync as existsSync49 } from "fs";
10167
+ import { readFile as readFile15 } from "fs/promises";
10168
+ import { existsSync as existsSync50 } from "fs";
10005
10169
  import "commander";
10006
10170
  import {
10007
- findProjectRoot as findProjectRoot30,
10008
- resolveHaivePaths as resolveHaivePaths27
10171
+ findProjectRoot as findProjectRoot31,
10172
+ resolveHaivePaths as resolveHaivePaths28
10009
10173
  } from "@hiveai/core";
10010
10174
  function registerMemoryImport(memory2) {
10011
10175
  memory2.command("import").description(
10012
10176
  "Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
10013
10177
  ).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) => {
10014
- const root = findProjectRoot30(opts.dir);
10015
- const paths = resolveHaivePaths27(root);
10016
- if (!existsSync49(paths.haiveDir)) {
10178
+ const root = findProjectRoot31(opts.dir);
10179
+ const paths = resolveHaivePaths28(root);
10180
+ if (!existsSync50(paths.haiveDir)) {
10017
10181
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10018
10182
  process.exitCode = 1;
10019
10183
  return;
10020
10184
  }
10021
- if (!existsSync49(opts.from)) {
10185
+ if (!existsSync50(opts.from)) {
10022
10186
  ui.error(`File not found: ${opts.from}`);
10023
10187
  process.exitCode = 1;
10024
10188
  return;
10025
10189
  }
10026
- const content = await readFile14(opts.from, "utf8");
10190
+ const content = await readFile15(opts.from, "utf8");
10027
10191
  const scope = opts.scope ?? "team";
10028
10192
  ui.info(`Preparing import from: ${opts.from} (scope=${scope})`);
10029
10193
  ui.info(`Content length: ${content.length} chars`);
@@ -10051,14 +10215,14 @@ function registerMemoryImport(memory2) {
10051
10215
  }
10052
10216
 
10053
10217
  // src/commands/memory-import-changelog.ts
10054
- import { existsSync as existsSync50 } from "fs";
10055
- import { readFile as readFile15, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
10056
- import path34 from "path";
10218
+ import { existsSync as existsSync51 } from "fs";
10219
+ import { readFile as readFile16, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
10220
+ import path35 from "path";
10057
10221
  import "commander";
10058
10222
  import {
10059
10223
  buildFrontmatter as buildFrontmatter9,
10060
- findProjectRoot as findProjectRoot31,
10061
- resolveHaivePaths as resolveHaivePaths28,
10224
+ findProjectRoot as findProjectRoot32,
10225
+ resolveHaivePaths as resolveHaivePaths29,
10062
10226
  serializeMemory as serializeMemory20
10063
10227
  } from "@hiveai/core";
10064
10228
  function parseChangelog(content) {
@@ -10123,15 +10287,15 @@ function registerMemoryImportChangelog(memory2) {
10123
10287
  "--versions <csv>",
10124
10288
  "only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
10125
10289
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
10126
- const root = findProjectRoot31(opts.dir);
10127
- const paths = resolveHaivePaths28(root);
10128
- const changelogPath = path34.resolve(root, opts.fromChangelog);
10129
- if (!existsSync50(changelogPath)) {
10290
+ const root = findProjectRoot32(opts.dir);
10291
+ const paths = resolveHaivePaths29(root);
10292
+ const changelogPath = path35.resolve(root, opts.fromChangelog);
10293
+ if (!existsSync51(changelogPath)) {
10130
10294
  ui.error(`CHANGELOG not found: ${changelogPath}`);
10131
10295
  process.exitCode = 1;
10132
10296
  return;
10133
10297
  }
10134
- const content = await readFile15(changelogPath, "utf8");
10298
+ const content = await readFile16(changelogPath, "utf8");
10135
10299
  let entries = parseChangelog(content);
10136
10300
  if (entries.length === 0) {
10137
10301
  ui.warn("No breaking changes, deprecations, or removals found in the CHANGELOG.");
@@ -10146,9 +10310,9 @@ function registerMemoryImportChangelog(memory2) {
10146
10310
  entries = entries.filter((e) => requested.includes(e.version));
10147
10311
  }
10148
10312
  }
10149
- const pkgName = opts.package ?? path34.basename(path34.dirname(changelogPath));
10313
+ const pkgName = opts.package ?? path35.basename(path35.dirname(changelogPath));
10150
10314
  const scope = opts.scope ?? "team";
10151
- const teamDir = path34.join(paths.memoriesDir, scope);
10315
+ const teamDir = path35.join(paths.memoriesDir, scope);
10152
10316
  await mkdir14(teamDir, { recursive: true });
10153
10317
  let saved = 0;
10154
10318
  for (const entry of entries) {
@@ -10171,7 +10335,7 @@ function registerMemoryImportChangelog(memory2) {
10171
10335
  lines.push("");
10172
10336
  }
10173
10337
  lines.push(
10174
- `**Source:** \`${path34.relative(root, changelogPath)}\`
10338
+ `**Source:** \`${path35.relative(root, changelogPath)}\`
10175
10339
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
10176
10340
  );
10177
10341
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -10186,11 +10350,11 @@ function registerMemoryImportChangelog(memory2) {
10186
10350
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
10187
10351
  `v${entry.version}`
10188
10352
  ],
10189
- paths: [path34.relative(root, changelogPath)],
10353
+ paths: [path35.relative(root, changelogPath)],
10190
10354
  topic: `changelog-${pkgName}-${entry.version}`
10191
10355
  });
10192
10356
  await writeFile23(
10193
- path34.join(teamDir, `${fm.id}.md`),
10357
+ path35.join(teamDir, `${fm.id}.md`),
10194
10358
  serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
10195
10359
  "utf8"
10196
10360
  );
@@ -10213,17 +10377,17 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
10213
10377
  }
10214
10378
 
10215
10379
  // src/commands/memory-digest.ts
10216
- import { existsSync as existsSync51 } from "fs";
10380
+ import { existsSync as existsSync53 } from "fs";
10217
10381
  import { writeFile as writeFile24 } from "fs/promises";
10218
- import path35 from "path";
10382
+ import path36 from "path";
10219
10383
  import "commander";
10220
10384
  import {
10221
10385
  deriveConfidence as deriveConfidence12,
10222
- findProjectRoot as findProjectRoot32,
10386
+ findProjectRoot as findProjectRoot33,
10223
10387
  getUsage as getUsage17,
10224
10388
  loadMemoriesFromDir as loadMemoriesFromDir26,
10225
10389
  loadUsageIndex as loadUsageIndex21,
10226
- resolveHaivePaths as resolveHaivePaths29
10390
+ resolveHaivePaths as resolveHaivePaths30
10227
10391
  } from "@hiveai/core";
10228
10392
  var CONFIDENCE_EMOJI = {
10229
10393
  unverified: "\u2B1C",
@@ -10236,9 +10400,9 @@ function registerMemoryDigest(program2) {
10236
10400
  program2.command("digest").description(
10237
10401
  "Generate a Markdown review digest of recently added or updated memories.\n\n Groups memories by type, shows confidence, status, read count, and anchor info.\n Each memory has action checkboxes (approve / reject / keep as-is) for peer review.\n\n Use this to do a bulk weekly review of team memories, or share with teammates\n as a pull-request attachment so humans can validate what the AI captured.\n\n Examples:\n haive memory digest # last 7 days, team scope\n haive memory digest --days 30 --scope all # last 30 days, all scopes\n haive memory digest --out review.md # write to file\n"
10238
10402
  ).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) => {
10239
- const root = findProjectRoot32(opts.dir);
10240
- const paths = resolveHaivePaths29(root);
10241
- if (!existsSync51(paths.memoriesDir)) {
10403
+ const root = findProjectRoot33(opts.dir);
10404
+ const paths = resolveHaivePaths30(root);
10405
+ if (!existsSync53(paths.memoriesDir)) {
10242
10406
  ui.error("No .ai/memories found. Run `haive init` first.");
10243
10407
  process.exitCode = 1;
10244
10408
  return;
@@ -10310,7 +10474,7 @@ function registerMemoryDigest(program2) {
10310
10474
  );
10311
10475
  const digest = lines.join("\n");
10312
10476
  if (opts.out) {
10313
- const outPath = path35.resolve(process.cwd(), opts.out);
10477
+ const outPath = path36.resolve(process.cwd(), opts.out);
10314
10478
  await writeFile24(outPath, digest, "utf8");
10315
10479
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
10316
10480
  } else {
@@ -10320,23 +10484,23 @@ function registerMemoryDigest(program2) {
10320
10484
  }
10321
10485
 
10322
10486
  // src/commands/session-end.ts
10323
- import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile16, rm as rm2 } from "fs/promises";
10324
- import { existsSync as existsSync53 } from "fs";
10487
+ import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile17, rm as rm2 } from "fs/promises";
10488
+ import { existsSync as existsSync54 } from "fs";
10325
10489
  import { spawn as spawn4 } from "child_process";
10326
- import path36 from "path";
10490
+ import path37 from "path";
10327
10491
  import "commander";
10328
10492
  import {
10329
10493
  buildFrontmatter as buildFrontmatter10,
10330
- findProjectRoot as findProjectRoot33,
10494
+ findProjectRoot as findProjectRoot34,
10331
10495
  loadMemoriesFromDir as loadMemoriesFromDir27,
10332
10496
  memoryFilePath as memoryFilePath9,
10333
- resolveHaivePaths as resolveHaivePaths30,
10497
+ resolveHaivePaths as resolveHaivePaths31,
10334
10498
  serializeMemory as serializeMemory21
10335
10499
  } from "@hiveai/core";
10336
10500
  async function buildAutoRecap(paths) {
10337
- const obsFile = path36.join(paths.haiveDir, ".cache", "observations.jsonl");
10338
- if (!existsSync53(obsFile)) return await buildGitAutoRecap(paths);
10339
- const raw = await readFile16(obsFile, "utf8").catch(() => "");
10501
+ const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
10502
+ if (!existsSync54(obsFile)) return await buildGitAutoRecap(paths);
10503
+ const raw = await readFile17(obsFile, "utf8").catch(() => "");
10340
10504
  if (!raw.trim()) return await buildGitAutoRecap(paths);
10341
10505
  const lines = raw.split("\n").filter(Boolean);
10342
10506
  const obs = [];
@@ -10524,9 +10688,9 @@ function registerSessionEnd(session2) {
10524
10688
  --next "Add integration tests for webhook signature validation"
10525
10689
  `
10526
10690
  ).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) => {
10527
- const root = findProjectRoot33(opts.dir);
10528
- const paths = resolveHaivePaths30(root);
10529
- if (!existsSync53(paths.haiveDir)) {
10691
+ const root = findProjectRoot34(opts.dir);
10692
+ const paths = resolveHaivePaths31(root);
10693
+ if (!existsSync54(paths.haiveDir)) {
10530
10694
  if (opts.auto || opts.quiet) return;
10531
10695
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10532
10696
  process.exitCode = 1;
@@ -10559,18 +10723,18 @@ function registerSessionEnd(session2) {
10559
10723
  });
10560
10724
  const topic = recapTopic2(scope, opts.module);
10561
10725
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
10562
- const missingPaths = filesTouched.filter((p) => !existsSync53(path36.resolve(root, p)));
10726
+ const missingPaths = filesTouched.filter((p) => !existsSync54(path37.resolve(root, p)));
10563
10727
  if (missingPaths.length > 0 && !opts.quiet) {
10564
10728
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
10565
10729
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
10566
10730
  }
10567
10731
  const cleanupObservations = async () => {
10568
10732
  if (!opts.auto) return;
10569
- const obsFile = path36.join(paths.haiveDir, ".cache", "observations.jsonl");
10570
- if (existsSync53(obsFile)) await rm2(obsFile).catch(() => {
10733
+ const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
10734
+ if (existsSync54(obsFile)) await rm2(obsFile).catch(() => {
10571
10735
  });
10572
10736
  };
10573
- if (existsSync53(paths.memoriesDir)) {
10737
+ if (existsSync54(paths.memoriesDir)) {
10574
10738
  const existing = await loadMemoriesFromDir27(paths.memoriesDir);
10575
10739
  const topicMatch = existing.find(
10576
10740
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
@@ -10591,7 +10755,7 @@ function registerSessionEnd(session2) {
10591
10755
  await cleanupObservations();
10592
10756
  if (!opts.quiet) {
10593
10757
  ui.success(`Session recap updated (revision #${revisionCount})`);
10594
- ui.info(`id=${fm.id} file=${path36.relative(root, topicMatch.filePath)}`);
10758
+ ui.info(`id=${fm.id} file=${path37.relative(root, topicMatch.filePath)}`);
10595
10759
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
10596
10760
  }
10597
10761
  return;
@@ -10608,12 +10772,12 @@ function registerSessionEnd(session2) {
10608
10772
  status: "validated"
10609
10773
  });
10610
10774
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
10611
- await mkdir15(path36.dirname(file), { recursive: true });
10775
+ await mkdir15(path37.dirname(file), { recursive: true });
10612
10776
  await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
10613
10777
  await cleanupObservations();
10614
10778
  if (!opts.quiet) {
10615
10779
  ui.success(`Session recap created`);
10616
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path36.relative(root, file)}`);
10780
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path37.relative(root, file)}`);
10617
10781
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
10618
10782
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
10619
10783
  }
@@ -10625,22 +10789,22 @@ function parseCsv5(value) {
10625
10789
  }
10626
10790
  function normalizeAnchorPath(root, filePath) {
10627
10791
  if (!filePath) return filePath;
10628
- if (!path36.isAbsolute(filePath)) return filePath;
10629
- const rel = path36.relative(root, filePath);
10792
+ if (!path37.isAbsolute(filePath)) return filePath;
10793
+ const rel = path37.relative(root, filePath);
10630
10794
  if (rel.startsWith("..")) return filePath;
10631
10795
  return rel;
10632
10796
  }
10633
10797
 
10634
10798
  // src/commands/snapshot.ts
10635
- import { existsSync as existsSync54 } from "fs";
10799
+ import { existsSync as existsSync55 } from "fs";
10636
10800
  import { readdir as readdir4 } from "fs/promises";
10637
- import path37 from "path";
10801
+ import path38 from "path";
10638
10802
  import "commander";
10639
10803
  import {
10640
10804
  diffContract,
10641
- findProjectRoot as findProjectRoot34,
10642
- loadConfig as loadConfig6,
10643
- resolveHaivePaths as resolveHaivePaths31,
10805
+ findProjectRoot as findProjectRoot35,
10806
+ loadConfig as loadConfig7,
10807
+ resolveHaivePaths as resolveHaivePaths32,
10644
10808
  snapshotContract
10645
10809
  } from "@hiveai/core";
10646
10810
  function registerSnapshot(program2) {
@@ -10665,16 +10829,16 @@ function registerSnapshot(program2) {
10665
10829
  "--format <format>",
10666
10830
  "contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
10667
10831
  ).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) => {
10668
- const root = findProjectRoot34(opts.dir);
10669
- const paths = resolveHaivePaths31(root);
10670
- if (!existsSync54(paths.haiveDir)) {
10832
+ const root = findProjectRoot35(opts.dir);
10833
+ const paths = resolveHaivePaths32(root);
10834
+ if (!existsSync55(paths.haiveDir)) {
10671
10835
  ui.error("No .ai/ found. Run `haive init` first.");
10672
10836
  process.exitCode = 1;
10673
10837
  return;
10674
10838
  }
10675
10839
  if (opts.list) {
10676
- const contractsDir = path37.join(paths.haiveDir, "contracts");
10677
- if (!existsSync54(contractsDir)) {
10840
+ const contractsDir = path38.join(paths.haiveDir, "contracts");
10841
+ if (!existsSync55(contractsDir)) {
10678
10842
  console.log(ui.dim("No contract snapshots found."));
10679
10843
  return;
10680
10844
  }
@@ -10694,7 +10858,7 @@ function registerSnapshot(program2) {
10694
10858
  }
10695
10859
  if (opts.diff) {
10696
10860
  if (!opts.name) {
10697
- const config2 = await loadConfig6(paths);
10861
+ const config2 = await loadConfig7(paths);
10698
10862
  const contracts = config2.contractFiles ?? [];
10699
10863
  if (contracts.length === 0) {
10700
10864
  ui.error("--diff requires --name, or configure contractFiles in haive.config.json");
@@ -10706,7 +10870,7 @@ function registerSnapshot(program2) {
10706
10870
  }
10707
10871
  return;
10708
10872
  }
10709
- const config = await loadConfig6(paths);
10873
+ const config = await loadConfig7(paths);
10710
10874
  const configured = (config.contractFiles ?? []).find((c) => c.name === opts.name);
10711
10875
  if (!configured && !opts.contract) {
10712
10876
  ui.error(
@@ -10729,7 +10893,7 @@ function registerSnapshot(program2) {
10729
10893
  return;
10730
10894
  }
10731
10895
  const contractPath = opts.contract;
10732
- const name = opts.name ?? path37.basename(contractPath, path37.extname(contractPath));
10896
+ const name = opts.name ?? path38.basename(contractPath, path38.extname(contractPath));
10733
10897
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
10734
10898
  const contract = { name, path: contractPath, format };
10735
10899
  try {
@@ -10784,8 +10948,8 @@ async function runDiff(root, haiveDir, contract) {
10784
10948
  }
10785
10949
  }
10786
10950
  function detectFormat(filePath) {
10787
- const ext = path37.extname(filePath).toLowerCase();
10788
- const base = path37.basename(filePath).toLowerCase();
10951
+ const ext = path38.extname(filePath).toLowerCase();
10952
+ const base = path38.basename(filePath).toLowerCase();
10789
10953
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
10790
10954
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
10791
10955
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -10798,16 +10962,16 @@ function detectFormat(filePath) {
10798
10962
  }
10799
10963
 
10800
10964
  // src/commands/hub.ts
10801
- import { existsSync as existsSync55 } from "fs";
10802
- import { mkdir as mkdir16, readFile as readFile17, writeFile as writeFile26, copyFile } from "fs/promises";
10803
- import path38 from "path";
10965
+ import { existsSync as existsSync56 } from "fs";
10966
+ import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile26, copyFile } from "fs/promises";
10967
+ import path39 from "path";
10804
10968
  import { spawnSync as spawnSync5 } from "child_process";
10805
10969
  import "commander";
10806
10970
  import {
10807
- findProjectRoot as findProjectRoot35,
10808
- loadConfig as loadConfig7,
10971
+ findProjectRoot as findProjectRoot36,
10972
+ loadConfig as loadConfig8,
10809
10973
  loadMemoriesFromDir as loadMemoriesFromDir28,
10810
- resolveHaivePaths as resolveHaivePaths32,
10974
+ resolveHaivePaths as resolveHaivePaths33,
10811
10975
  saveConfig as saveConfig3,
10812
10976
  serializeMemory as serializeMemory23
10813
10977
  } from "@hiveai/core";
@@ -10819,7 +10983,7 @@ function registerHub(program2) {
10819
10983
  hub.command("init <hubPath>").description(
10820
10984
  "Initialize a new team-knowledge hub repo at <hubPath>.\n\n Creates a git repo with a .ai/ directory structure ready for shared memories.\n\n Example:\n haive hub init ../team-hub\n haive hub init /srv/git/team-knowledge\n"
10821
10985
  ).action(async (hubPath) => {
10822
- const absPath = path38.resolve(hubPath);
10986
+ const absPath = path39.resolve(hubPath);
10823
10987
  await mkdir16(absPath, { recursive: true });
10824
10988
  const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
10825
10989
  if (gitCheck.status !== 0) {
@@ -10830,10 +10994,10 @@ function registerHub(program2) {
10830
10994
  return;
10831
10995
  }
10832
10996
  }
10833
- const sharedDir = path38.join(absPath, ".ai", "memories", "shared");
10997
+ const sharedDir = path39.join(absPath, ".ai", "memories", "shared");
10834
10998
  await mkdir16(sharedDir, { recursive: true });
10835
10999
  await writeFile26(
10836
- path38.join(absPath, ".ai", "README.md"),
11000
+ path39.join(absPath, ".ai", "README.md"),
10837
11001
  `# hAIve Team Knowledge Hub
10838
11002
 
10839
11003
  This repo is a shared knowledge hub for hAIve.
@@ -10855,7 +11019,7 @@ haive hub pull # import into a project
10855
11019
  "utf8"
10856
11020
  );
10857
11021
  await writeFile26(
10858
- path38.join(absPath, ".gitignore"),
11022
+ path39.join(absPath, ".gitignore"),
10859
11023
  ".ai/.cache/\n.ai/memories/personal/\n",
10860
11024
  "utf8"
10861
11025
  );
@@ -10870,7 +11034,7 @@ haive hub pull # import into a project
10870
11034
  `
10871
11035
  Next steps:
10872
11036
  1. Add hubPath to your project's .ai/haive.config.json:
10873
- { "hubPath": "${path38.relative(process.cwd(), absPath)}" }
11037
+ { "hubPath": "${path39.relative(process.cwd(), absPath)}" }
10874
11038
  2. Run \`haive hub push\` to publish your shared memories
10875
11039
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
10876
11040
  `
@@ -10889,9 +11053,9 @@ Next steps:
10889
11053
  haive hub push --commit --message "feat: add payment API contract memories"
10890
11054
  `
10891
11055
  ).option("-d, --dir <dir>", "project root").option("--commit", "auto-commit to the hub repo after pushing").option("--message <msg>", "commit message for the hub (used with --commit)").action(async (opts) => {
10892
- const root = findProjectRoot35(opts.dir);
10893
- const paths = resolveHaivePaths32(root);
10894
- const config = await loadConfig7(paths);
11056
+ const root = findProjectRoot36(opts.dir);
11057
+ const paths = resolveHaivePaths33(root);
11058
+ const config = await loadConfig8(paths);
10895
11059
  if (!config.hubPath) {
10896
11060
  ui.error(
10897
11061
  'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
@@ -10899,14 +11063,14 @@ Next steps:
10899
11063
  process.exitCode = 1;
10900
11064
  return;
10901
11065
  }
10902
- const hubRoot = path38.resolve(root, config.hubPath);
10903
- if (!existsSync55(hubRoot)) {
11066
+ const hubRoot = path39.resolve(root, config.hubPath);
11067
+ if (!existsSync56(hubRoot)) {
10904
11068
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
10905
11069
  process.exitCode = 1;
10906
11070
  return;
10907
11071
  }
10908
- const projectName = path38.basename(root);
10909
- const destDir = path38.join(hubRoot, ".ai", "memories", "shared", projectName);
11072
+ const projectName = path39.basename(root);
11073
+ const destDir = path39.join(hubRoot, ".ai", "memories", "shared", projectName);
10910
11074
  await mkdir16(destDir, { recursive: true });
10911
11075
  const all = await loadMemoriesFromDir28(paths.memoriesDir);
10912
11076
  const shared = all.filter(
@@ -10925,7 +11089,7 @@ Next steps:
10925
11089
  for (const { memory: memory2 } of shared) {
10926
11090
  const fm = memory2.frontmatter;
10927
11091
  const fileName = `${fm.id}.md`;
10928
- const destPath = path38.join(destDir, fileName);
11092
+ const destPath = path39.join(destDir, fileName);
10929
11093
  await writeFile26(destPath, serializeMemory23(memory2), "utf8");
10930
11094
  pushed++;
10931
11095
  }
@@ -10933,7 +11097,7 @@ Next steps:
10933
11097
  console.log(ui.dim(` Location: ${destDir}`));
10934
11098
  if (opts.commit) {
10935
11099
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
10936
- spawnSync5("git", ["add", path38.join(".ai", "memories", "shared", projectName)], {
11100
+ spawnSync5("git", ["add", path39.join(".ai", "memories", "shared", projectName)], {
10937
11101
  cwd: hubRoot
10938
11102
  });
10939
11103
  const commit = spawnSync5("git", ["commit", "-m", message], {
@@ -10958,9 +11122,9 @@ Next steps:
10958
11122
  hub.command("pull").description(
10959
11123
  "Pull shared memories from the hub into this project.\n\n Imports all memories from hub/.ai/memories/shared/ EXCEPT this project's own.\n Imported memories land in .ai/memories/shared/<source-project-name>/.\n\n Examples:\n haive hub pull\n"
10960
11124
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
10961
- const root = findProjectRoot35(opts.dir);
10962
- const paths = resolveHaivePaths32(root);
10963
- const config = await loadConfig7(paths);
11125
+ const root = findProjectRoot36(opts.dir);
11126
+ const paths = resolveHaivePaths33(root);
11127
+ const config = await loadConfig8(paths);
10964
11128
  if (!config.hubPath) {
10965
11129
  ui.error(
10966
11130
  'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
@@ -10968,13 +11132,13 @@ Next steps:
10968
11132
  process.exitCode = 1;
10969
11133
  return;
10970
11134
  }
10971
- const hubRoot = path38.resolve(root, config.hubPath);
10972
- const hubSharedDir = path38.join(hubRoot, ".ai", "memories", "shared");
10973
- if (!existsSync55(hubSharedDir)) {
11135
+ const hubRoot = path39.resolve(root, config.hubPath);
11136
+ const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
11137
+ if (!existsSync56(hubSharedDir)) {
10974
11138
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
10975
11139
  return;
10976
11140
  }
10977
- const projectName = path38.basename(root);
11141
+ const projectName = path39.basename(root);
10978
11142
  const { readdir: readdir7 } = await import("fs/promises");
10979
11143
  const projectDirs = (await readdir7(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
10980
11144
  if (projectDirs.length === 0) {
@@ -10984,17 +11148,17 @@ Next steps:
10984
11148
  let totalImported = 0;
10985
11149
  let totalUpdated = 0;
10986
11150
  for (const sourceName of projectDirs) {
10987
- const sourceDir = path38.join(hubSharedDir, sourceName);
10988
- const destDir = path38.join(paths.memoriesDir, "shared", sourceName);
11151
+ const sourceDir = path39.join(hubSharedDir, sourceName);
11152
+ const destDir = path39.join(paths.memoriesDir, "shared", sourceName);
10989
11153
  await mkdir16(destDir, { recursive: true });
10990
11154
  const sourceFiles = (await readdir7(sourceDir)).filter((f) => f.endsWith(".md"));
10991
11155
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
10992
11156
  const existingInDest = await loadDir(destDir);
10993
11157
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
10994
11158
  for (const file of sourceFiles) {
10995
- const srcPath = path38.join(sourceDir, file);
10996
- const destPath = path38.join(destDir, file);
10997
- const fileContent = await readFile17(srcPath, "utf8");
11159
+ const srcPath = path39.join(sourceDir, file);
11160
+ const destPath = path39.join(destDir, file);
11161
+ const fileContent = await readFile18(srcPath, "utf8");
10998
11162
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
10999
11163
  if (!alreadyTagged) {
11000
11164
  await copyFile(srcPath, destPath);
@@ -11017,21 +11181,21 @@ Next steps:
11017
11181
  );
11018
11182
  });
11019
11183
  hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
11020
- const root = findProjectRoot35(opts.dir);
11021
- const paths = resolveHaivePaths32(root);
11022
- const config = await loadConfig7(paths);
11184
+ const root = findProjectRoot36(opts.dir);
11185
+ const paths = resolveHaivePaths33(root);
11186
+ const config = await loadConfig8(paths);
11023
11187
  console.log(ui.bold("Hub status"));
11024
11188
  console.log(
11025
11189
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
11026
11190
  );
11027
- const sharedDir = path38.join(paths.memoriesDir, "shared");
11028
- if (existsSync55(sharedDir)) {
11191
+ const sharedDir = path39.join(paths.memoriesDir, "shared");
11192
+ if (existsSync56(sharedDir)) {
11029
11193
  const { readdir: readdir7 } = await import("fs/promises");
11030
11194
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
11031
11195
  console.log(`
11032
11196
  Imported from ${sources.length} source(s):`);
11033
11197
  for (const src of sources) {
11034
- const files = (await readdir7(path38.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
11198
+ const files = (await readdir7(path39.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
11035
11199
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
11036
11200
  }
11037
11201
  } else {
@@ -11046,7 +11210,7 @@ Next steps:
11046
11210
  if (outgoing.length > 0) {
11047
11211
  console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
11048
11212
  }
11049
- void readFile17;
11213
+ void readFile18;
11050
11214
  void writeFile26;
11051
11215
  void saveConfig3;
11052
11216
  });
@@ -11054,17 +11218,17 @@ Next steps:
11054
11218
 
11055
11219
  // src/commands/stats.ts
11056
11220
  import "commander";
11057
- import { existsSync as existsSync56 } from "fs";
11221
+ import { existsSync as existsSync57 } from "fs";
11058
11222
  import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
11059
- import path39 from "path";
11223
+ import path40 from "path";
11060
11224
  import {
11061
11225
  aggregateUsage,
11062
- findProjectRoot as findProjectRoot36,
11226
+ findProjectRoot as findProjectRoot37,
11063
11227
  loadMemoriesFromDir as loadMemoriesFromDir29,
11064
11228
  loadUsageIndex as loadUsageIndex23,
11065
11229
  parseSince,
11066
11230
  readUsageEvents as readUsageEvents2,
11067
- resolveHaivePaths as resolveHaivePaths33,
11231
+ resolveHaivePaths as resolveHaivePaths34,
11068
11232
  usageLogSize
11069
11233
  } from "@hiveai/core";
11070
11234
  function registerStats(program2) {
@@ -11073,8 +11237,8 @@ function registerStats(program2) {
11073
11237
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
11074
11238
  void 0
11075
11239
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
11076
- const root = findProjectRoot36(opts.dir);
11077
- const paths = resolveHaivePaths33(root);
11240
+ const root = findProjectRoot37(opts.dir);
11241
+ const paths = resolveHaivePaths34(root);
11078
11242
  if (opts.exportReport) {
11079
11243
  await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
11080
11244
  return;
@@ -11128,11 +11292,11 @@ function registerStats(program2) {
11128
11292
  });
11129
11293
  }
11130
11294
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11131
- const outAbs = path39.isAbsolute(outRelative) ? path39.resolve(outRelative) : path39.resolve(root, outRelative);
11295
+ const outAbs = path40.isAbsolute(outRelative) ? path40.resolve(outRelative) : path40.resolve(root, outRelative);
11132
11296
  const size = await usageLogSize(paths);
11133
11297
  let events = await readUsageEvents2(paths);
11134
11298
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
11135
- if (existsSync56(paths.memoriesDir)) {
11299
+ if (existsSync57(paths.memoriesDir)) {
11136
11300
  const mems = await loadMemoriesFromDir29(paths.memoriesDir);
11137
11301
  for (const { memory: memory2 } of mems) {
11138
11302
  const fm = memory2.frontmatter;
@@ -11163,7 +11327,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11163
11327
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
11164
11328
  events = [];
11165
11329
  }
11166
- await mkdir17(path39.dirname(outAbs), { recursive: true });
11330
+ await mkdir17(path40.dirname(outAbs), { recursive: true });
11167
11331
  const payload = {
11168
11332
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
11169
11333
  project_root: root,
@@ -11227,13 +11391,13 @@ import { performance } from "perf_hooks";
11227
11391
  import "commander";
11228
11392
  import {
11229
11393
  estimateTokens as estimateTokens3,
11230
- findProjectRoot as findProjectRoot37,
11231
- resolveHaivePaths as resolveHaivePaths34
11394
+ findProjectRoot as findProjectRoot38,
11395
+ resolveHaivePaths as resolveHaivePaths35
11232
11396
  } from "@hiveai/core";
11233
11397
  function registerBench(program2) {
11234
11398
  program2.command("bench").description("Self-test the local hAIve setup: runs core MCP tools against this project and reports latency + payload size.").option("-t, --task <task>", "task description for ranking-aware tools", "audit dependencies for security risks").option("--json", "emit JSON instead of a table", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11235
- const root = findProjectRoot37(opts.dir);
11236
- const paths = resolveHaivePaths34(root);
11399
+ const root = findProjectRoot38(opts.dir);
11400
+ const paths = resolveHaivePaths35(root);
11237
11401
  const ctx = { paths };
11238
11402
  const task = opts.task ?? "audit dependencies for security risks";
11239
11403
  const scenarios = [
@@ -11352,11 +11516,11 @@ function summarize(name, t0, payload, notes) {
11352
11516
  }
11353
11517
 
11354
11518
  // src/commands/benchmark.ts
11355
- import { existsSync as existsSync57 } from "fs";
11356
- import { readdir as readdir5, readFile as readFile18, writeFile as writeFile28 } from "fs/promises";
11357
- import path40 from "path";
11519
+ import { existsSync as existsSync58 } from "fs";
11520
+ import { readdir as readdir5, readFile as readFile19, writeFile as writeFile28 } from "fs/promises";
11521
+ import path41 from "path";
11358
11522
  import "commander";
11359
- import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot38 } from "@hiveai/core";
11523
+ import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot39 } from "@hiveai/core";
11360
11524
  function registerBenchmark(program2) {
11361
11525
  const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
11362
11526
  benchmark.command("report").description("Summarize BENCHMARK_AGENT_REPORT.md files from a paired hAIve/plain agent benchmark.").option("-d, --dir <dir>", "benchmark root", "benchmarks/agent-benchmark").option("--out <file>", "write a Markdown report").option("--json", "emit JSON", false).action(async (opts) => {
@@ -11369,9 +11533,9 @@ function registerBenchmark(program2) {
11369
11533
  }
11370
11534
  const markdown = renderMarkdown(root, summary, rows);
11371
11535
  if (opts.out) {
11372
- const outFile = path40.isAbsolute(opts.out) ? opts.out : path40.join(root, opts.out);
11536
+ const outFile = path41.isAbsolute(opts.out) ? opts.out : path41.join(root, opts.out);
11373
11537
  await writeFile28(outFile, markdown, "utf8");
11374
- ui.success(`wrote ${path40.relative(process.cwd(), outFile)}`);
11538
+ ui.success(`wrote ${path41.relative(process.cwd(), outFile)}`);
11375
11539
  return;
11376
11540
  }
11377
11541
  console.log(markdown);
@@ -11395,20 +11559,20 @@ function registerBenchmark(program2) {
11395
11559
  }
11396
11560
  function resolveBenchmarkRoot(dir) {
11397
11561
  const candidate = dir ?? "benchmarks/agent-benchmark";
11398
- if (path40.isAbsolute(candidate)) return candidate;
11399
- const projectRoot = findProjectRoot38(process.cwd());
11400
- return path40.join(projectRoot, candidate);
11562
+ if (path41.isAbsolute(candidate)) return candidate;
11563
+ const projectRoot = findProjectRoot39(process.cwd());
11564
+ return path41.join(projectRoot, candidate);
11401
11565
  }
11402
11566
  async function collectRows(root) {
11403
- if (!existsSync57(root)) throw new Error(`Benchmark directory not found: ${root}`);
11567
+ if (!existsSync58(root)) throw new Error(`Benchmark directory not found: ${root}`);
11404
11568
  const entries = await readdir5(root, { withFileTypes: true });
11405
11569
  const rows = [];
11406
11570
  for (const entry of entries) {
11407
11571
  if (!entry.isDirectory()) continue;
11408
- const fixtureDir = path40.join(root, entry.name);
11409
- const reportFile = path40.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
11410
- if (!existsSync57(reportFile)) continue;
11411
- const report = await readFile18(reportFile, "utf8");
11572
+ const fixtureDir = path41.join(root, entry.name);
11573
+ const reportFile = path41.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
11574
+ if (!existsSync58(reportFile)) continue;
11575
+ const report = await readFile19(reportFile, "utf8");
11412
11576
  rows.push(parseAgentReport(entry.name, report));
11413
11577
  }
11414
11578
  return rows.sort((a, b) => a.fixture.localeCompare(b.fixture));
@@ -11498,19 +11662,19 @@ function escapeRegExp(value) {
11498
11662
 
11499
11663
  // src/commands/memory-suggest.ts
11500
11664
  import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
11501
- import { existsSync as existsSync58 } from "fs";
11502
- import path41 from "path";
11665
+ import { existsSync as existsSync59 } from "fs";
11666
+ import path43 from "path";
11503
11667
  import "commander";
11504
11668
  import {
11505
11669
  aggregateUsage as aggregateUsage2,
11506
11670
  buildFrontmatter as buildFrontmatter11,
11507
- findProjectRoot as findProjectRoot39,
11508
- loadConfig as loadConfig8,
11671
+ findProjectRoot as findProjectRoot40,
11672
+ loadConfig as loadConfig9,
11509
11673
  loadMemoriesFromDir as loadMemoriesFromDir30,
11510
11674
  memoryFilePath as memoryFilePath10,
11511
11675
  parseSince as parseSince2,
11512
11676
  readUsageEvents as readUsageEvents3,
11513
- resolveHaivePaths as resolveHaivePaths35,
11677
+ resolveHaivePaths as resolveHaivePaths36,
11514
11678
  serializeMemory as serializeMemory24
11515
11679
  } from "@hiveai/core";
11516
11680
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
@@ -11527,8 +11691,8 @@ function registerMemorySuggest(memory2) {
11527
11691
  memory2.command("suggest").description(
11528
11692
  "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."
11529
11693
  ).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) => {
11530
- const root = findProjectRoot39(opts.dir);
11531
- const paths = resolveHaivePaths35(root);
11694
+ const root = findProjectRoot40(opts.dir);
11695
+ const paths = resolveHaivePaths36(root);
11532
11696
  const events = await readUsageEvents3(paths);
11533
11697
  if (events.length === 0) {
11534
11698
  if (opts.json) {
@@ -11566,7 +11730,7 @@ function registerMemorySuggest(memory2) {
11566
11730
  inferred_type: inferType(v.tools, query)
11567
11731
  })).sort((a, b) => b.count - a.count);
11568
11732
  if (opts.autoSave) {
11569
- const config = await loadConfig8(paths);
11733
+ const config = await loadConfig9(paths);
11570
11734
  const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
11571
11735
  const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
11572
11736
  const status = config.defaultStatus === "validated" ? "validated" : "draft";
@@ -11577,7 +11741,7 @@ function registerMemorySuggest(memory2) {
11577
11741
  }
11578
11742
  const created = [];
11579
11743
  const skipped = [];
11580
- const existing = existsSync58(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
11744
+ const existing = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
11581
11745
  for (const s of top) {
11582
11746
  const slug = slugify2(s.query);
11583
11747
  if (!slug) {
@@ -11600,13 +11764,13 @@ function registerMemorySuggest(memory2) {
11600
11764
  });
11601
11765
  const body = renderTemplate(s, fm.id, status);
11602
11766
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
11603
- await mkdir18(path41.dirname(file), { recursive: true });
11604
- if (existsSync58(file)) {
11605
- skipped.push({ query: s.query, reason: `file already exists at ${path41.relative(root, file)}` });
11767
+ await mkdir18(path43.dirname(file), { recursive: true });
11768
+ if (existsSync59(file)) {
11769
+ skipped.push({ query: s.query, reason: `file already exists at ${path43.relative(root, file)}` });
11606
11770
  continue;
11607
11771
  }
11608
11772
  await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
11609
- created.push({ id: fm.id, file: path41.relative(root, file), query: s.query });
11773
+ created.push({ id: fm.id, file: path43.relative(root, file), query: s.query });
11610
11774
  }
11611
11775
  if (opts.json) {
11612
11776
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -11704,16 +11868,16 @@ function truncate2(text, max) {
11704
11868
  }
11705
11869
 
11706
11870
  // src/commands/memory-archive.ts
11707
- import { existsSync as existsSync59 } from "fs";
11871
+ import { existsSync as existsSync60 } from "fs";
11708
11872
  import { writeFile as writeFile30 } from "fs/promises";
11709
- import path43 from "path";
11873
+ import path44 from "path";
11710
11874
  import "commander";
11711
11875
  import {
11712
- findProjectRoot as findProjectRoot40,
11876
+ findProjectRoot as findProjectRoot41,
11713
11877
  getUsage as getUsage18,
11714
11878
  loadMemoriesFromDir as loadMemoriesFromDir31,
11715
11879
  loadUsageIndex as loadUsageIndex24,
11716
- resolveHaivePaths as resolveHaivePaths36,
11880
+ resolveHaivePaths as resolveHaivePaths37,
11717
11881
  serializeMemory as serializeMemory25
11718
11882
  } from "@hiveai/core";
11719
11883
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
@@ -11721,9 +11885,9 @@ function registerMemoryArchive(memory2) {
11721
11885
  memory2.command("archive").description(
11722
11886
  "Archive obsolete memories: marks status='deprecated' for memories not read in N days\n whose anchored paths have all disappeared (or have no anchor at all).\n\n Defaults to a DRY RUN \u2014 pass --apply to actually rewrite files.\n Targets `attempt` memories by default since they age the fastest.\n\n Recover later with `haive memory edit <id>` to set status back to validated."
11723
11887
  ).option("--since <window>", "minimum age since last read (e.g. '180d', '6m')", "180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").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) => {
11724
- const root = findProjectRoot40(opts.dir);
11725
- const paths = resolveHaivePaths36(root);
11726
- if (!existsSync59(paths.memoriesDir)) {
11888
+ const root = findProjectRoot41(opts.dir);
11889
+ const paths = resolveHaivePaths37(root);
11890
+ if (!existsSync60(paths.memoriesDir)) {
11727
11891
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
11728
11892
  process.exitCode = 1;
11729
11893
  return;
@@ -11744,7 +11908,7 @@ function registerMemoryArchive(memory2) {
11744
11908
  if (typeFilter && fm.type !== typeFilter) continue;
11745
11909
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
11746
11910
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
11747
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(path43.join(paths.root, p)));
11911
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync60(path44.join(paths.root, p)));
11748
11912
  const isAnchorless = !hasAnyAnchor;
11749
11913
  if (!isAnchorless && !allPathsGone) continue;
11750
11914
  const u = getUsage18(usage, fm.id);
@@ -11818,33 +11982,33 @@ function parseDays(input) {
11818
11982
  }
11819
11983
 
11820
11984
  // src/commands/doctor.ts
11821
- import { existsSync as existsSync60, statSync } from "fs";
11822
- import { readFile as readFile19, stat, writeFile as writeFile31 } from "fs/promises";
11823
- import path44 from "path";
11985
+ import { existsSync as existsSync61, statSync } from "fs";
11986
+ import { readFile as readFile20, stat, writeFile as writeFile31 } from "fs/promises";
11987
+ import path45 from "path";
11824
11988
  import { execFileSync, execSync as execSync3 } from "child_process";
11825
11989
  import "commander";
11826
11990
  import {
11827
11991
  codeMapPath as codeMapPath2,
11828
- findProjectRoot as findProjectRoot41,
11992
+ findProjectRoot as findProjectRoot42,
11829
11993
  getUsage as getUsage19,
11830
11994
  loadCodeMap as loadCodeMap6,
11831
- loadConfig as loadConfig9,
11995
+ loadConfig as loadConfig10,
11832
11996
  loadMemoriesFromDir as loadMemoriesFromDir33,
11833
11997
  loadUsageIndex as loadUsageIndex25,
11834
11998
  readUsageEvents as readUsageEvents4,
11835
- resolveHaivePaths as resolveHaivePaths37
11999
+ resolveHaivePaths as resolveHaivePaths38
11836
12000
  } from "@hiveai/core";
11837
12001
  var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
11838
12002
  function registerDoctor(program2) {
11839
12003
  program2.command("doctor").description(
11840
12004
  "Analyze the local hAIve setup and emit actionable recommendations.\n\n Inspects: project-context status, memory health (stale/anchorless/decay/pending),\n code-map freshness, usage log signals (low-hit briefings, repeated empty searches).\n\n Read-only by default. Pass --fix to apply safe autopilot repairs."
11841
12005
  ).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("--dry-run", "with --fix, show delegated repairs without applying them", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11842
- const root = findProjectRoot41(opts.dir);
11843
- const paths = resolveHaivePaths37(root);
12006
+ const root = findProjectRoot42(opts.dir);
12007
+ const paths = resolveHaivePaths38(root);
11844
12008
  const findings = [];
11845
12009
  const repairs = [];
11846
- const config = await loadConfig9(paths);
11847
- if (!existsSync60(paths.haiveDir)) {
12010
+ const config = await loadConfig10(paths);
12011
+ if (!existsSync61(paths.haiveDir)) {
11848
12012
  findings.push({
11849
12013
  severity: "error",
11850
12014
  code: "not-initialized",
@@ -11865,7 +12029,7 @@ function registerDoctor(program2) {
11865
12029
  })
11866
12030
  );
11867
12031
  }
11868
- if (!existsSync60(paths.projectContext)) {
12032
+ if (!existsSync61(paths.projectContext)) {
11869
12033
  findings.push({
11870
12034
  severity: "warn",
11871
12035
  code: "no-project-context",
@@ -11873,8 +12037,8 @@ function registerDoctor(program2) {
11873
12037
  fix: "haive init"
11874
12038
  });
11875
12039
  } else {
11876
- const { readFile: readFile21 } = await import("fs/promises");
11877
- const content = await readFile21(paths.projectContext, "utf8");
12040
+ const { readFile: readFile23 } = await import("fs/promises");
12041
+ const content = await readFile23(paths.projectContext, "utf8");
11878
12042
  const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `haive init`");
11879
12043
  if (isTemplate) {
11880
12044
  findings.push({
@@ -11894,7 +12058,7 @@ function registerDoctor(program2) {
11894
12058
  });
11895
12059
  }
11896
12060
  }
11897
- const memories = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
12061
+ const memories = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
11898
12062
  const now = Date.now();
11899
12063
  if (memories.length === 0) {
11900
12064
  findings.push({
@@ -12035,12 +12199,12 @@ function registerDoctor(program2) {
12035
12199
  }
12036
12200
  }
12037
12201
  if (config.enforcement?.requireBriefingFirst) {
12038
- const claudeSettings = path44.join(root, ".claude", "settings.local.json");
12202
+ const claudeSettings = path45.join(root, ".claude", "settings.local.json");
12039
12203
  let hasClaudeEnforcement = false;
12040
- if (existsSync60(claudeSettings)) {
12204
+ if (existsSync61(claudeSettings)) {
12041
12205
  try {
12042
- const { readFile: readFile21 } = await import("fs/promises");
12043
- const raw = await readFile21(claudeSettings, "utf8");
12206
+ const { readFile: readFile23 } = await import("fs/promises");
12207
+ const raw = await readFile23(claudeSettings, "utf8");
12044
12208
  hasClaudeEnforcement = raw.includes("haive enforce session-start") && raw.includes("haive enforce pre-tool-use");
12045
12209
  } catch {
12046
12210
  hasClaudeEnforcement = false;
@@ -12063,14 +12227,14 @@ function registerDoctor(program2) {
12063
12227
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12064
12228
  });
12065
12229
  }
12066
- findings.push(...await collectInstallFindings(root, "0.9.27"));
12230
+ findings.push(...await collectInstallFindings(root, "0.9.29"));
12067
12231
  try {
12068
12232
  const legacyRaw = execSync3("haive-mcp --version", {
12069
12233
  encoding: "utf8",
12070
12234
  timeout: 3e3,
12071
12235
  stdio: ["ignore", "pipe", "ignore"]
12072
12236
  }).trim();
12073
- const cliVersion = "0.9.27";
12237
+ const cliVersion = "0.9.29";
12074
12238
  if (legacyRaw && legacyRaw !== cliVersion) {
12075
12239
  findings.push({
12076
12240
  severity: "warn",
@@ -12086,17 +12250,17 @@ npm uninstall -g @hiveai/mcp`
12086
12250
  }
12087
12251
  {
12088
12252
  const configPaths = [
12089
- path44.join(root, ".mcp.json"),
12090
- path44.join(root, ".cursor", "mcp.json"),
12091
- path44.join(root, ".vscode", "mcp.json")
12253
+ path45.join(root, ".mcp.json"),
12254
+ path45.join(root, ".cursor", "mcp.json"),
12255
+ path45.join(root, ".vscode", "mcp.json")
12092
12256
  ];
12093
12257
  const staleConfigs = [];
12094
12258
  for (const cfgPath of configPaths) {
12095
- if (!existsSync60(cfgPath)) continue;
12259
+ if (!existsSync61(cfgPath)) continue;
12096
12260
  try {
12097
- const raw = await readFile19(cfgPath, "utf8");
12261
+ const raw = await readFile20(cfgPath, "utf8");
12098
12262
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
12099
- staleConfigs.push(path44.relative(root, cfgPath));
12263
+ staleConfigs.push(path45.relative(root, cfgPath));
12100
12264
  if (opts.fix && !opts.dryRun) {
12101
12265
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
12102
12266
  await writeFile31(cfgPath, updated, "utf8");
@@ -12386,9 +12550,9 @@ which -a haive`
12386
12550
  ".vscode/mcp.json"
12387
12551
  ];
12388
12552
  for (const rel of integrationFiles) {
12389
- const file = path44.join(root, rel);
12390
- if (!existsSync60(file)) continue;
12391
- const text = await readFile19(file, "utf8").catch(() => "");
12553
+ const file = path45.join(root, rel);
12554
+ if (!existsSync61(file)) continue;
12555
+ const text = await readFile20(file, "utf8").catch(() => "");
12392
12556
  for (const bin of extractAbsoluteHaiveBins(text)) {
12393
12557
  const version = versionForBinary(bin);
12394
12558
  if (!version) {
@@ -12412,7 +12576,7 @@ which -a haive`
12412
12576
  }
12413
12577
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
12414
12578
  const findings = [];
12415
- const rootPkg = await readJson(path44.join(root, "package.json"));
12579
+ const rootPkg = await readJson(path45.join(root, "package.json"));
12416
12580
  const workspacePackages = [
12417
12581
  "packages/core/package.json",
12418
12582
  "packages/embeddings/package.json",
@@ -12421,7 +12585,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
12421
12585
  ];
12422
12586
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
12423
12587
  rel,
12424
- pkg: await readJson(path44.join(root, rel))
12588
+ pkg: await readJson(path45.join(root, rel))
12425
12589
  })))).filter((item) => item.pkg);
12426
12590
  const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
12427
12591
  if (!isHaiveWorkspace) return findings;
@@ -12483,9 +12647,9 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
12483
12647
  }
12484
12648
  }
12485
12649
  async function readJson(file) {
12486
- if (!existsSync60(file)) return null;
12650
+ if (!existsSync61(file)) return null;
12487
12651
  try {
12488
- return JSON.parse(await readFile19(file, "utf8"));
12652
+ return JSON.parse(await readFile20(file, "utf8"));
12489
12653
  } catch {
12490
12654
  return null;
12491
12655
  }
@@ -12530,22 +12694,22 @@ function extractAbsoluteHaiveBins(text) {
12530
12694
  }
12531
12695
 
12532
12696
  // src/commands/playback.ts
12533
- import { existsSync as existsSync61 } from "fs";
12697
+ import { existsSync as existsSync63 } from "fs";
12534
12698
  import "commander";
12535
12699
  import {
12536
- findProjectRoot as findProjectRoot42,
12700
+ findProjectRoot as findProjectRoot43,
12537
12701
  loadMemoriesFromDir as loadMemoriesFromDir34,
12538
12702
  parseSince as parseSince3,
12539
12703
  readUsageEvents as readUsageEvents5,
12540
- resolveHaivePaths as resolveHaivePaths38
12704
+ resolveHaivePaths as resolveHaivePaths39
12541
12705
  } from "@hiveai/core";
12542
12706
  var MS_PER_MINUTE = 6e4;
12543
12707
  function registerPlayback(program2) {
12544
12708
  program2.command("playback").description(
12545
12709
  "Replay past sessions from the usage log. For each session, show:\n - tool calls (what kind, how many)\n - briefing tasks asked\n - memories that have been created since then (that the session didn't have)\n\n Useful to ask 'would today's haive have helped past me on this task?'"
12546
12710
  ).option("--since <window>", "limit to events in this window (e.g. '7d')", "30d").option("--session-gap <minutes>", "minutes of inactivity that splits a session", "30").option("--limit <n>", "show at most this many sessions (newest first)", "10").option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
12547
- const root = findProjectRoot42(opts.dir);
12548
- const paths = resolveHaivePaths38(root);
12711
+ const root = findProjectRoot43(opts.dir);
12712
+ const paths = resolveHaivePaths39(root);
12549
12713
  const events = await readUsageEvents5(paths);
12550
12714
  if (events.length === 0) {
12551
12715
  if (opts.json) {
@@ -12560,7 +12724,7 @@ function registerPlayback(program2) {
12560
12724
  const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
12561
12725
  const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
12562
12726
  const sessions = bucketSessions(filtered, gapMs);
12563
- const all = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
12727
+ const all = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
12564
12728
  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);
12565
12729
  const enriched = sessions.map((s, i) => {
12566
12730
  const startMs = Date.parse(s.start);
@@ -12650,8 +12814,8 @@ function truncate3(text, max) {
12650
12814
  import { spawn as spawn5 } from "child_process";
12651
12815
  import "commander";
12652
12816
  import {
12653
- findProjectRoot as findProjectRoot43,
12654
- resolveHaivePaths as resolveHaivePaths39
12817
+ findProjectRoot as findProjectRoot44,
12818
+ resolveHaivePaths as resolveHaivePaths40
12655
12819
  } from "@hiveai/core";
12656
12820
  function registerPrecommit(program2) {
12657
12821
  program2.command("precommit").description(
@@ -12661,8 +12825,8 @@ function registerPrecommit(program2) {
12661
12825
  "'any' | 'high-confidence' (default) | 'never' (report only)",
12662
12826
  "high-confidence"
12663
12827
  ).option("--no-semantic", "disable semantic search in anti-patterns matching").option("--json", "emit JSON instead of human-readable output", false).option("--paths <paths...>", "explicit paths to check (skips git diff)").option("-d, --dir <dir>", "project root").action(async (opts) => {
12664
- const root = findProjectRoot43(opts.dir);
12665
- const paths = resolveHaivePaths39(root);
12828
+ const root = findProjectRoot44(opts.dir);
12829
+ const paths = resolveHaivePaths40(root);
12666
12830
  const ctx = { paths };
12667
12831
  let diff = "";
12668
12832
  let touchedPaths = opts.paths ?? [];
@@ -12792,12 +12956,12 @@ function runCommand3(cmd, args, cwd) {
12792
12956
  }
12793
12957
 
12794
12958
  // src/commands/welcome.ts
12795
- import { existsSync as existsSync63 } from "fs";
12959
+ import { existsSync as existsSync64 } from "fs";
12796
12960
  import "commander";
12797
12961
  import {
12798
- findProjectRoot as findProjectRoot44,
12962
+ findProjectRoot as findProjectRoot45,
12799
12963
  loadMemoriesFromDir as loadMemoriesFromDir35,
12800
- resolveHaivePaths as resolveHaivePaths40
12964
+ resolveHaivePaths as resolveHaivePaths41
12801
12965
  } from "@hiveai/core";
12802
12966
  var TYPE_RANK = {
12803
12967
  skill: 0,
@@ -12812,9 +12976,9 @@ function registerWelcome(program2) {
12812
12976
  program2.command("welcome").description(
12813
12977
  "Onboarding checklist: ranks validated/proposed **team** memories by type.\nUse after `haive init` so new devs skim institutional knowledge quickly.\n\n haive welcome\n haive welcome --limit 15\n"
12814
12978
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
12815
- const root = findProjectRoot44(opts.dir);
12816
- const paths = resolveHaivePaths40(root);
12817
- if (!existsSync63(paths.memoriesDir)) {
12979
+ const root = findProjectRoot45(opts.dir);
12980
+ const paths = resolveHaivePaths41(root);
12981
+ if (!existsSync64(paths.memoriesDir)) {
12818
12982
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
12819
12983
  process.exitCode = 1;
12820
12984
  return;
@@ -12873,27 +13037,27 @@ function registerMemorySuggestTopic(memory2) {
12873
13037
  }
12874
13038
 
12875
13039
  // src/commands/resolve-project.ts
12876
- import path45 from "path";
13040
+ import path46 from "path";
12877
13041
  import "commander";
12878
13042
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
12879
13043
  function registerResolveProject(program2) {
12880
13044
  program2.command("resolve-project").description(
12881
13045
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
12882
13046
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
12883
- const info = resolveProjectInfo2({ cwd: path45.resolve(opts.dir) });
13047
+ const info = resolveProjectInfo2({ cwd: path46.resolve(opts.dir) });
12884
13048
  console.log(JSON.stringify({ ok: true, info }, null, 2));
12885
13049
  });
12886
13050
  }
12887
13051
 
12888
13052
  // src/commands/runtime-journal.ts
12889
- import { existsSync as existsSync64 } from "fs";
12890
- import path46 from "path";
13053
+ import { existsSync as existsSync65 } from "fs";
13054
+ import path47 from "path";
12891
13055
  import "commander";
12892
13056
  import {
12893
13057
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
12894
- findProjectRoot as findProjectRoot45,
13058
+ findProjectRoot as findProjectRoot46,
12895
13059
  readRuntimeJournalTail as readRuntimeJournalTail2,
12896
- resolveHaivePaths as resolveHaivePaths41
13060
+ resolveHaivePaths as resolveHaivePaths42
12897
13061
  } from "@hiveai/core";
12898
13062
  function registerRuntime(program2) {
12899
13063
  const runtime = program2.command("runtime").description(
@@ -12901,18 +13065,18 @@ function registerRuntime(program2) {
12901
13065
  );
12902
13066
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
12903
13067
  journal.command("append").description("Append one JSON line to .ai/.runtime/session-journal.ndjson").argument("<message>", "short text to log").option("-k, --kind <kind>", "note | session_end | mcp", "note").option("-d, --dir <dir>", "project root", process.cwd()).action(async (message, opts) => {
12904
- const root = path46.resolve(opts.dir ?? process.cwd());
12905
- const paths = resolveHaivePaths41(findProjectRoot45(root));
13068
+ const root = path47.resolve(opts.dir ?? process.cwd());
13069
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
12906
13070
  const raw = opts.kind ?? "note";
12907
13071
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
12908
13072
  await appendRuntimeJournalEntry3(paths, { kind, message });
12909
- ui.success(`Appended to ${path46.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
13073
+ ui.success(`Appended to ${path47.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
12910
13074
  });
12911
13075
  journal.command("tail").description("Print the last N entries from the runtime session journal as JSON").option("-n, --limit <n>", "number of lines", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
12912
- const root = path46.resolve(opts.dir ?? process.cwd());
12913
- const paths = resolveHaivePaths41(findProjectRoot45(root));
13076
+ const root = path47.resolve(opts.dir ?? process.cwd());
13077
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
12914
13078
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
12915
- if (!existsSync64(paths.haiveDir)) {
13079
+ if (!existsSync65(paths.haiveDir)) {
12916
13080
  ui.error("No .ai/ \u2014 run `haive init` first.");
12917
13081
  process.exitCode = 1;
12918
13082
  return;
@@ -12927,13 +13091,13 @@ function registerRuntime(program2) {
12927
13091
  }
12928
13092
 
12929
13093
  // src/commands/memory-timeline.ts
12930
- import { existsSync as existsSync65 } from "fs";
12931
- import path47 from "path";
13094
+ import { existsSync as existsSync66 } from "fs";
13095
+ import path48 from "path";
12932
13096
  import "commander";
12933
13097
  import {
12934
13098
  collectTimelineEntries as collectTimelineEntries2,
12935
- findProjectRoot as findProjectRoot46,
12936
- resolveHaivePaths as resolveHaivePaths42
13099
+ findProjectRoot as findProjectRoot47,
13100
+ resolveHaivePaths as resolveHaivePaths43
12937
13101
  } from "@hiveai/core";
12938
13102
  function registerMemoryTimeline(memory2) {
12939
13103
  memory2.command("timeline").description(
@@ -12944,9 +13108,9 @@ function registerMemoryTimeline(memory2) {
12944
13108
  process.exitCode = 1;
12945
13109
  return;
12946
13110
  }
12947
- const root = path47.resolve(opts.dir ?? process.cwd());
12948
- const paths = resolveHaivePaths42(findProjectRoot46(root));
12949
- if (!existsSync65(paths.memoriesDir)) {
13111
+ const root = path48.resolve(opts.dir ?? process.cwd());
13112
+ const paths = resolveHaivePaths43(findProjectRoot47(root));
13113
+ if (!existsSync66(paths.memoriesDir)) {
12950
13114
  ui.error("No memories \u2014 run `haive init`.");
12951
13115
  process.exitCode = 1;
12952
13116
  return;
@@ -12964,14 +13128,14 @@ function registerMemoryTimeline(memory2) {
12964
13128
  }
12965
13129
 
12966
13130
  // src/commands/memory-conflict-candidates.ts
12967
- import { existsSync as existsSync66 } from "fs";
12968
- import path48 from "path";
13131
+ import { existsSync as existsSync67 } from "fs";
13132
+ import path49 from "path";
12969
13133
  import "commander";
12970
13134
  import {
12971
13135
  findLexicalConflictPairs as findLexicalConflictPairs2,
12972
13136
  findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
12973
- findProjectRoot as findProjectRoot47,
12974
- resolveHaivePaths as resolveHaivePaths43
13137
+ findProjectRoot as findProjectRoot48,
13138
+ resolveHaivePaths as resolveHaivePaths44
12975
13139
  } from "@hiveai/core";
12976
13140
  function parseTypes(csv) {
12977
13141
  const allowed = ["decision", "architecture", "convention", "gotcha"];
@@ -12987,9 +13151,9 @@ function registerMemoryConflictCandidates(memory2) {
12987
13151
  "decision,architecture,convention,gotcha (lexical scan)",
12988
13152
  "decision,architecture"
12989
13153
  ).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) => {
12990
- const root = path48.resolve(opts.dir ?? process.cwd());
12991
- const paths = resolveHaivePaths43(findProjectRoot47(root));
12992
- if (!existsSync66(paths.memoriesDir)) {
13154
+ const root = path49.resolve(opts.dir ?? process.cwd());
13155
+ const paths = resolveHaivePaths44(findProjectRoot48(root));
13156
+ if (!existsSync67(paths.memoriesDir)) {
12993
13157
  ui.error("No memories \u2014 run `haive init`.");
12994
13158
  process.exitCode = 1;
12995
13159
  return;
@@ -13025,24 +13189,24 @@ function registerMemoryConflictCandidates(memory2) {
13025
13189
 
13026
13190
  // src/commands/enforce.ts
13027
13191
  import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
13028
- import { existsSync as existsSync67, statSync as statSync2 } from "fs";
13029
- import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
13030
- import path49 from "path";
13192
+ import { existsSync as existsSync68, statSync as statSync2 } from "fs";
13193
+ import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
13194
+ import path50 from "path";
13031
13195
  import "commander";
13032
13196
  import {
13033
- findProjectRoot as findProjectRoot48,
13197
+ findProjectRoot as findProjectRoot49,
13034
13198
  hasRecentBriefingMarker,
13035
13199
  isFreshIsoDate,
13036
- loadConfig as loadConfig10,
13200
+ loadConfig as loadConfig11,
13037
13201
  loadMemoriesFromDir as loadMemoriesFromDir36,
13038
13202
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
13039
13203
  readRecentBriefingMarker,
13040
13204
  resolveBriefingBudget as resolveBriefingBudget3,
13041
- resolveHaivePaths as resolveHaivePaths44,
13205
+ resolveHaivePaths as resolveHaivePaths45,
13042
13206
  saveConfig as saveConfig4,
13043
13207
  SESSION_RECAP_TTL_MS,
13044
13208
  verifyAnchor as verifyAnchor4,
13045
- writeBriefingMarker as writeBriefingMarker2
13209
+ writeBriefingMarker as writeBriefingMarker3
13046
13210
  } from "@hiveai/core";
13047
13211
  var MAX_STDIN_BYTES2 = 256 * 1024;
13048
13212
  var ENFORCE_HOOK_MARKER = "# hAIve enforcement hook";
@@ -13051,10 +13215,10 @@ function registerEnforce(program2) {
13051
13215
  "Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
13052
13216
  );
13053
13217
  enforce.command("install").description("Install hAIve enforcement across MCP config, git hooks, CI template, and supported client hooks.").option("-d, --dir <dir>", "project root").option("--no-git", "skip git pre-commit/pre-push enforcement hooks").option("--no-claude", "skip Claude Code hooks").option("--no-ci", "skip GitHub Actions enforcement workflow").action(async (opts) => {
13054
- const root = findProjectRoot48(opts.dir);
13055
- const paths = resolveHaivePaths44(root);
13218
+ const root = findProjectRoot49(opts.dir);
13219
+ const paths = resolveHaivePaths45(root);
13056
13220
  await mkdir19(paths.haiveDir, { recursive: true });
13057
- const current = await loadConfig10(paths);
13221
+ const current = await loadConfig11(paths);
13058
13222
  await saveConfig4(paths, {
13059
13223
  ...current,
13060
13224
  enforcement: {
@@ -13077,7 +13241,7 @@ function registerEnforce(program2) {
13077
13241
  if (opts.claude !== false) {
13078
13242
  try {
13079
13243
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
13080
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path49.relative(root, result.settingsPath)})`);
13244
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path50.relative(root, result.settingsPath)})`);
13081
13245
  } catch (err) {
13082
13246
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
13083
13247
  }
@@ -13096,21 +13260,21 @@ function registerEnforce(program2) {
13096
13260
  if (report.should_block) process.exit(2);
13097
13261
  });
13098
13262
  enforce.command("cleanup").description("Remove generated hAIve runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
13099
- const root = findProjectRoot48(opts.dir);
13100
- const paths = resolveHaivePaths44(root);
13101
- const cacheDir = path49.join(paths.haiveDir, ".cache");
13102
- if (existsSync67(cacheDir)) {
13103
- if (opts.dryRun) ui.info(`would clean ${path49.relative(root, cacheDir)} (preserving .gitignore)`);
13263
+ const root = findProjectRoot49(opts.dir);
13264
+ const paths = resolveHaivePaths45(root);
13265
+ const cacheDir = path50.join(paths.haiveDir, ".cache");
13266
+ if (existsSync68(cacheDir)) {
13267
+ if (opts.dryRun) ui.info(`would clean ${path50.relative(root, cacheDir)} (preserving .gitignore)`);
13104
13268
  else {
13105
13269
  const removed = await cleanupCacheDir(cacheDir);
13106
- ui.success(`cleaned ${path49.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13270
+ ui.success(`cleaned ${path50.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13107
13271
  }
13108
13272
  }
13109
- if (existsSync67(paths.runtimeDir)) {
13110
- if (opts.dryRun) ui.info(`would clean ${path49.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
13273
+ if (existsSync68(paths.runtimeDir)) {
13274
+ if (opts.dryRun) ui.info(`would clean ${path50.relative(root, paths.runtimeDir)} (preserving briefing markers)`);
13111
13275
  else {
13112
13276
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
13113
- ui.success(`cleaned ${path49.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13277
+ ui.success(`cleaned ${path50.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
13114
13278
  }
13115
13279
  }
13116
13280
  });
@@ -13123,8 +13287,8 @@ function registerEnforce(program2) {
13123
13287
  const payload = await readHookPayload();
13124
13288
  const root = resolveRoot(opts.dir, payload);
13125
13289
  if (!root) return;
13126
- const paths = resolveHaivePaths44(root);
13127
- if (!existsSync67(paths.haiveDir)) return;
13290
+ const paths = resolveHaivePaths45(root);
13291
+ if (!existsSync68(paths.haiveDir)) return;
13128
13292
  await mkdir19(paths.runtimeDir, { recursive: true });
13129
13293
  const sessionId = opts.sessionId ?? payload.session_id;
13130
13294
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
@@ -13152,7 +13316,7 @@ function registerEnforce(program2) {
13152
13316
  },
13153
13317
  { paths }
13154
13318
  );
13155
- await writeBriefingMarker2(paths, {
13319
+ await writeBriefingMarker3(paths, {
13156
13320
  sessionId,
13157
13321
  task,
13158
13322
  source: opts.source ?? "claude-session-start",
@@ -13186,8 +13350,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13186
13350
  const payload = await readHookPayload();
13187
13351
  const root = resolveRoot(opts.dir, payload);
13188
13352
  if (!root) return;
13189
- const paths = resolveHaivePaths44(root);
13190
- if (!existsSync67(paths.haiveDir)) return;
13353
+ const paths = resolveHaivePaths45(root);
13354
+ if (!existsSync68(paths.haiveDir)) return;
13191
13355
  if (!isWriteLikeTool(payload)) return;
13192
13356
  const ok = await hasRecentBriefingMarker(paths, payload.session_id);
13193
13357
  if (ok) {
@@ -13229,15 +13393,15 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13229
13393
  });
13230
13394
  }
13231
13395
  async function runWithEnforcement(command, args, opts) {
13232
- const root = findProjectRoot48(opts.dir);
13233
- const paths = resolveHaivePaths44(root);
13234
- if (!existsSync67(paths.haiveDir)) {
13396
+ const root = findProjectRoot49(opts.dir);
13397
+ const paths = resolveHaivePaths45(root);
13398
+ if (!existsSync68(paths.haiveDir)) {
13235
13399
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
13236
13400
  process.exit(1);
13237
13401
  }
13238
13402
  const sessionId = `haive-run-${process.pid}-${Date.now()}`;
13239
13403
  const task = opts.task ?? `Run agent command: ${[command, ...args].join(" ")}`;
13240
- await writeBriefingMarker2(paths, {
13404
+ await writeBriefingMarker3(paths, {
13241
13405
  sessionId,
13242
13406
  task,
13243
13407
  source: "haive-run"
@@ -13250,7 +13414,7 @@ async function runWithEnforcement(command, args, opts) {
13250
13414
  process.exit(2);
13251
13415
  }
13252
13416
  ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
13253
- ui.info(`Briefing written to ${path49.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
13417
+ ui.info(`Briefing written to ${path50.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
13254
13418
  const child = spawn6(command, args, {
13255
13419
  cwd: root,
13256
13420
  stdio: "inherit",
@@ -13294,15 +13458,15 @@ async function writeWrapperBriefing(paths, sessionId, task) {
13294
13458
  min_semantic_score: 0.25,
13295
13459
  budget_preset: "quick"
13296
13460
  }, { paths });
13297
- await writeBriefingMarker2(paths, {
13461
+ await writeBriefingMarker3(paths, {
13298
13462
  sessionId,
13299
13463
  task,
13300
13464
  source: "haive-run",
13301
13465
  memoryIds: briefing.memories.map((m) => m.id)
13302
13466
  });
13303
- const dir = path49.join(paths.runtimeDir, "enforcement", "briefings");
13467
+ const dir = path50.join(paths.runtimeDir, "enforcement", "briefings");
13304
13468
  await mkdir19(dir, { recursive: true });
13305
- const file = path49.join(dir, `${sessionId}.md`);
13469
+ const file = path50.join(dir, `${sessionId}.md`);
13306
13470
  const parts = [
13307
13471
  "# hAIve Briefing",
13308
13472
  "",
@@ -13324,10 +13488,10 @@ async function writeWrapperBriefing(paths, sessionId, task) {
13324
13488
  return file;
13325
13489
  }
13326
13490
  async function buildEnforcementReport(dir, stage, sessionId) {
13327
- const root = findProjectRoot48(dir);
13328
- const paths = resolveHaivePaths44(root);
13329
- const initialized = existsSync67(paths.haiveDir);
13330
- const config = initialized ? await loadConfig10(paths) : {};
13491
+ const root = findProjectRoot49(dir);
13492
+ const paths = resolveHaivePaths45(root);
13493
+ const initialized = existsSync68(paths.haiveDir);
13494
+ const config = initialized ? await loadConfig11(paths) : {};
13331
13495
  if (initialized) await applyLightweightRepairs(root, paths);
13332
13496
  const mode = config.enforcement?.mode ?? "strict";
13333
13497
  const findings = [];
@@ -13357,7 +13521,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
13357
13521
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
13358
13522
  });
13359
13523
  }
13360
- findings.push(...await inspectIntegrationVersions(root, "0.9.27"));
13524
+ findings.push(...await inspectIntegrationVersions(root, "0.9.29"));
13361
13525
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
13362
13526
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
13363
13527
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -13427,7 +13591,7 @@ function withCategories(report) {
13427
13591
  };
13428
13592
  }
13429
13593
  async function hasRecentSessionRecap(paths) {
13430
- if (!existsSync67(paths.memoriesDir)) return false;
13594
+ if (!existsSync68(paths.memoriesDir)) return false;
13431
13595
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13432
13596
  return all.some(({ memory: memory2 }) => {
13433
13597
  const fm = memory2.frontmatter;
@@ -13436,7 +13600,7 @@ async function hasRecentSessionRecap(paths) {
13436
13600
  });
13437
13601
  }
13438
13602
  async function verifyMemoryPolicy(paths, config) {
13439
- if (!existsSync67(paths.memoriesDir)) return [];
13603
+ if (!existsSync68(paths.memoriesDir)) return [];
13440
13604
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13441
13605
  const findings = [];
13442
13606
  const staleImportant = [];
@@ -13474,7 +13638,7 @@ async function verifyMemoryPolicy(paths, config) {
13474
13638
  return findings;
13475
13639
  }
13476
13640
  async function verifyDecisionCoverage(paths, stage, sessionId) {
13477
- if (!existsSync67(paths.memoriesDir)) return [];
13641
+ if (!existsSync68(paths.memoriesDir)) return [];
13478
13642
  const changedFiles = await getChangedFiles(paths.root, stage);
13479
13643
  if (changedFiles.length === 0) {
13480
13644
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
@@ -13573,16 +13737,16 @@ async function cleanupRuntimeDir(runtimeDir) {
13573
13737
  for (const entry of entries) {
13574
13738
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
13575
13739
  if (entry.name === "enforcement") {
13576
- removed += await cleanupEnforcementDir(path49.join(runtimeDir, entry.name));
13740
+ removed += await cleanupEnforcementDir(path50.join(runtimeDir, entry.name));
13577
13741
  continue;
13578
13742
  }
13579
- await rm3(path49.join(runtimeDir, entry.name), { recursive: true, force: true });
13743
+ await rm3(path50.join(runtimeDir, entry.name), { recursive: true, force: true });
13580
13744
  removed++;
13581
13745
  }
13582
- await writeFile33(path49.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
13583
- if (!existsSync67(path49.join(runtimeDir, "README.md"))) {
13746
+ await writeFile33(path50.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
13747
+ if (!existsSync68(path50.join(runtimeDir, "README.md"))) {
13584
13748
  await writeFile33(
13585
- path49.join(runtimeDir, "README.md"),
13749
+ path50.join(runtimeDir, "README.md"),
13586
13750
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
13587
13751
  "utf8"
13588
13752
  );
@@ -13595,10 +13759,10 @@ async function cleanupCacheDir(cacheDir) {
13595
13759
  const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
13596
13760
  for (const entry of entries) {
13597
13761
  if (entry.name === ".gitignore") continue;
13598
- await rm3(path49.join(cacheDir, entry.name), { recursive: true, force: true });
13762
+ await rm3(path50.join(cacheDir, entry.name), { recursive: true, force: true });
13599
13763
  removed++;
13600
13764
  }
13601
- await writeFile33(path49.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
13765
+ await writeFile33(path50.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
13602
13766
  return removed;
13603
13767
  }
13604
13768
  async function cleanupEnforcementDir(enforcementDir) {
@@ -13606,7 +13770,7 @@ async function cleanupEnforcementDir(enforcementDir) {
13606
13770
  const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
13607
13771
  for (const entry of entries) {
13608
13772
  if (entry.name === "briefings") continue;
13609
- await rm3(path49.join(enforcementDir, entry.name), { recursive: true, force: true });
13773
+ await rm3(path50.join(enforcementDir, entry.name), { recursive: true, force: true });
13610
13774
  removed++;
13611
13775
  }
13612
13776
  return removed;
@@ -13622,9 +13786,9 @@ async function inspectIntegrationVersions(root, expectedVersion) {
13622
13786
  ];
13623
13787
  const findings = [];
13624
13788
  for (const rel of files) {
13625
- const file = path49.join(root, rel);
13626
- if (!existsSync67(file)) continue;
13627
- const text = await readFile20(file, "utf8").catch(() => "");
13789
+ const file = path50.join(root, rel);
13790
+ if (!existsSync68(file)) continue;
13791
+ const text = await readFile21(file, "utf8").catch(() => "");
13628
13792
  for (const bin of extractAbsoluteHaiveBins2(text)) {
13629
13793
  const version = versionForBinary2(bin);
13630
13794
  if (!version) {
@@ -13718,8 +13882,8 @@ function buildScore(findings, threshold = 80) {
13718
13882
  };
13719
13883
  }
13720
13884
  async function installGitEnforcement(root) {
13721
- const hooksDir = path49.join(root, ".git", "hooks");
13722
- if (!existsSync67(path49.join(root, ".git"))) {
13885
+ const hooksDir = path50.join(root, ".git", "hooks");
13886
+ if (!existsSync68(path50.join(root, ".git"))) {
13723
13887
  ui.warn("No .git directory found; git enforcement hooks skipped.");
13724
13888
  return;
13725
13889
  }
@@ -13741,9 +13905,9 @@ haive enforce check --stage pre-push --dir . || exit $?
13741
13905
  }
13742
13906
  ];
13743
13907
  for (const hook of hooks) {
13744
- const file = path49.join(hooksDir, hook.name);
13745
- if (existsSync67(file)) {
13746
- const current = await readFile20(file, "utf8").catch(() => "");
13908
+ const file = path50.join(hooksDir, hook.name);
13909
+ if (existsSync68(file)) {
13910
+ const current = await readFile21(file, "utf8").catch(() => "");
13747
13911
  if (current.includes(ENFORCE_HOOK_MARKER)) {
13748
13912
  await writeFile33(file, hook.body, "utf8");
13749
13913
  } else {
@@ -13759,9 +13923,9 @@ ${hook.body}`, "utf8");
13759
13923
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
13760
13924
  }
13761
13925
  async function installCiEnforcement(root) {
13762
- const workflowPath = path49.join(root, ".github", "workflows", "haive-enforcement.yml");
13763
- await mkdir19(path49.dirname(workflowPath), { recursive: true });
13764
- if (existsSync67(workflowPath)) {
13926
+ const workflowPath = path50.join(root, ".github", "workflows", "haive-enforcement.yml");
13927
+ await mkdir19(path50.dirname(workflowPath), { recursive: true });
13928
+ if (existsSync68(workflowPath)) {
13765
13929
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
13766
13930
  return;
13767
13931
  }
@@ -13789,7 +13953,7 @@ jobs:
13789
13953
  - name: Enforce hAIve policy
13790
13954
  run: haive enforce ci
13791
13955
  `, "utf8");
13792
- ui.success(`Created ${path49.relative(root, workflowPath)}`);
13956
+ ui.success(`Created ${path50.relative(root, workflowPath)}`);
13793
13957
  }
13794
13958
  function printReport(report, json, explain = false) {
13795
13959
  if (json) {
@@ -13849,7 +14013,7 @@ async function readHookPayload() {
13849
14013
  }
13850
14014
  function resolveRoot(dir, payload) {
13851
14015
  try {
13852
- return findProjectRoot48(dir ?? payload.cwd);
14016
+ return findProjectRoot49(dir ?? payload.cwd);
13853
14017
  } catch {
13854
14018
  return null;
13855
14019
  }
@@ -13886,11 +14050,11 @@ function extractToolPaths(payload, root) {
13886
14050
  }
13887
14051
  function normalizeToolPath(file, root) {
13888
14052
  const normalized = file.replace(/\\/g, "/");
13889
- if (!path49.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
13890
- return path49.relative(root, normalized).replace(/\\/g, "/");
14053
+ if (!path50.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
14054
+ return path50.relative(root, normalized).replace(/\\/g, "/");
13891
14055
  }
13892
14056
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
13893
- if (!existsSync67(paths.memoriesDir)) return [];
14057
+ if (!existsSync68(paths.memoriesDir)) return [];
13894
14058
  const marker = await readRecentBriefingMarker(paths, sessionId);
13895
14059
  const consulted = new Set(marker?.memory_ids ?? []);
13896
14060
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
@@ -13963,8 +14127,8 @@ function registerRun(program2) {
13963
14127
  }
13964
14128
 
13965
14129
  // src/index.ts
13966
- var program = new Command51();
13967
- program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.27").option("--advanced", "show maintenance and experimental commands in help");
14130
+ var program = new Command52();
14131
+ program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.29").option("--advanced", "show maintenance and experimental commands in help");
13968
14132
  registerInit(program);
13969
14133
  registerWelcome(program);
13970
14134
  registerResolveProject(program);
@@ -13998,6 +14162,7 @@ registerMemoryApprove(memory);
13998
14162
  registerMemoryUpdate(memory);
13999
14163
  registerMemoryHot(memory);
14000
14164
  registerMemoryTried(memory);
14165
+ registerMemorySeed(memory);
14001
14166
  registerMemoryImport(memory);
14002
14167
  registerMemoryImportChangelog(memory);
14003
14168
  registerMemoryDigest(memory);