@hiveai/cli 0.9.28 → 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";
@@ -199,7 +199,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
199
199
  if (!f) continue;
200
200
  counts.set(f, (counts.get(f) ?? 0) + 1);
201
201
  }
202
- let entries = [...counts.entries()].map(([path50, changes]) => ({ path: path50, changes }));
202
+ let entries = [...counts.entries()].map(([path51, changes]) => ({ path: path51, changes }));
203
203
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
204
204
  if (lowerPaths.length > 0) {
205
205
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -7190,7 +7190,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
7190
7190
  };
7191
7191
  }
7192
7192
  var SERVER_NAME = "haive";
7193
- var SERVER_VERSION = "0.9.28";
7193
+ var SERVER_VERSION = "0.9.29";
7194
7194
  function jsonResult(data) {
7195
7195
  return {
7196
7196
  content: [
@@ -9612,21 +9612,118 @@ function parseCsv4(value) {
9612
9612
  return value.split(",").map((s) => s.trim()).filter(Boolean);
9613
9613
  }
9614
9614
 
9615
- // src/commands/memory-pending.ts
9615
+ // src/commands/memory-seed.ts
9616
+ import { readFile as readFile13 } from "fs/promises";
9616
9617
  import { existsSync as existsSync41 } from "fs";
9617
9618
  import path27 from "path";
9618
9619
  import "commander";
9619
9620
  import {
9620
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,
9621
9718
  getUsage as getUsage14,
9622
9719
  loadUsageIndex as loadUsageIndex16,
9623
- resolveHaivePaths as resolveHaivePaths20
9720
+ resolveHaivePaths as resolveHaivePaths21
9624
9721
  } from "@hiveai/core";
9625
9722
  function registerMemoryPending(memory2) {
9626
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) => {
9627
- const root = findProjectRoot23(opts.dir);
9628
- const paths = resolveHaivePaths20(root);
9629
- if (!existsSync41(paths.memoriesDir)) {
9724
+ const root = findProjectRoot24(opts.dir);
9725
+ const paths = resolveHaivePaths21(root);
9726
+ if (!existsSync43(paths.memoriesDir)) {
9630
9727
  ui.error(`No .ai/memories at ${root}.`);
9631
9728
  process.exitCode = 1;
9632
9729
  return;
@@ -9660,7 +9757,7 @@ function registerMemoryPending(memory2) {
9660
9757
  console.log(
9661
9758
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
9662
9759
  );
9663
- console.log(` ${ui.dim(path27.relative(root, filePath))}`);
9760
+ console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9664
9761
  }
9665
9762
  if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
9666
9763
  console.log();
@@ -9675,7 +9772,7 @@ function registerMemoryPending(memory2) {
9675
9772
  console.log(
9676
9773
  ` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
9677
9774
  );
9678
- console.log(` ${ui.dim(path27.relative(root, filePath))}`);
9775
+ console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9679
9776
  }
9680
9777
  console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
9681
9778
  }
@@ -9684,24 +9781,24 @@ function registerMemoryPending(memory2) {
9684
9781
  }
9685
9782
 
9686
9783
  // src/commands/memory-query.ts
9687
- import { existsSync as existsSync43 } from "fs";
9688
- import path28 from "path";
9784
+ import { existsSync as existsSync44 } from "fs";
9785
+ import path29 from "path";
9689
9786
  import "commander";
9690
9787
  import {
9691
9788
  extractSnippet as extractSnippet2,
9692
- findProjectRoot as findProjectRoot24,
9789
+ findProjectRoot as findProjectRoot25,
9693
9790
  literalMatchesAllTokens as literalMatchesAllTokens3,
9694
9791
  literalMatchesAnyToken as literalMatchesAnyToken4,
9695
9792
  pickSnippetNeedle as pickSnippetNeedle2,
9696
- resolveHaivePaths as resolveHaivePaths21,
9793
+ resolveHaivePaths as resolveHaivePaths22,
9697
9794
  tokenizeQuery as tokenizeQuery6,
9698
9795
  trackReads as trackReads4
9699
9796
  } from "@hiveai/core";
9700
9797
  function registerMemoryQuery(memory2) {
9701
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) => {
9702
- const root = findProjectRoot24(opts.dir);
9703
- const paths = resolveHaivePaths21(root);
9704
- if (!existsSync43(paths.memoriesDir)) {
9799
+ const root = findProjectRoot25(opts.dir);
9800
+ const paths = resolveHaivePaths22(root);
9801
+ if (!existsSync44(paths.memoriesDir)) {
9705
9802
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
9706
9803
  process.exitCode = 1;
9707
9804
  return;
@@ -9742,7 +9839,7 @@ function registerMemoryQuery(memory2) {
9742
9839
  const fm = mem.frontmatter;
9743
9840
  const statusBadge = ui.statusBadge(fm.status);
9744
9841
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
9745
- console.log(` ${ui.dim(path28.relative(root, filePath))}`);
9842
+ console.log(` ${ui.dim(path29.relative(root, filePath))}`);
9746
9843
  const snippet = extractSnippet2(mem.body, snippetNeedle);
9747
9844
  if (snippet) console.log(` ${snippet}`);
9748
9845
  }
@@ -9760,21 +9857,21 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
9760
9857
 
9761
9858
  // src/commands/memory-reject.ts
9762
9859
  import { writeFile as writeFile20 } from "fs/promises";
9763
- import { existsSync as existsSync44 } from "fs";
9860
+ import { existsSync as existsSync45 } from "fs";
9764
9861
  import "commander";
9765
9862
  import {
9766
- findProjectRoot as findProjectRoot25,
9863
+ findProjectRoot as findProjectRoot26,
9767
9864
  loadUsageIndex as loadUsageIndex17,
9768
9865
  recordRejection as recordRejection2,
9769
- resolveHaivePaths as resolveHaivePaths22,
9866
+ resolveHaivePaths as resolveHaivePaths23,
9770
9867
  saveUsageIndex as saveUsageIndex3,
9771
9868
  serializeMemory as serializeMemory18
9772
9869
  } from "@hiveai/core";
9773
9870
  function registerMemoryReject(memory2) {
9774
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) => {
9775
- const root = findProjectRoot25(opts.dir);
9776
- const paths = resolveHaivePaths22(root);
9777
- if (!existsSync44(paths.memoriesDir)) {
9872
+ const root = findProjectRoot26(opts.dir);
9873
+ const paths = resolveHaivePaths23(root);
9874
+ if (!existsSync45(paths.memoriesDir)) {
9778
9875
  ui.error(`No .ai/memories at ${root}.`);
9779
9876
  process.exitCode = 1;
9780
9877
  return;
@@ -9810,22 +9907,22 @@ function registerMemoryReject(memory2) {
9810
9907
  }
9811
9908
 
9812
9909
  // src/commands/memory-rm.ts
9813
- import { existsSync as existsSync45 } from "fs";
9910
+ import { existsSync as existsSync46 } from "fs";
9814
9911
  import { unlink as unlink3 } from "fs/promises";
9815
- import path29 from "path";
9912
+ import path30 from "path";
9816
9913
  import { createInterface as createInterface2 } from "readline/promises";
9817
9914
  import "commander";
9818
9915
  import {
9819
- findProjectRoot as findProjectRoot26,
9916
+ findProjectRoot as findProjectRoot27,
9820
9917
  loadUsageIndex as loadUsageIndex18,
9821
- resolveHaivePaths as resolveHaivePaths23,
9918
+ resolveHaivePaths as resolveHaivePaths24,
9822
9919
  saveUsageIndex as saveUsageIndex4
9823
9920
  } from "@hiveai/core";
9824
9921
  function registerMemoryRm(memory2) {
9825
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) => {
9826
- const root = findProjectRoot26(opts.dir);
9827
- const paths = resolveHaivePaths23(root);
9828
- if (!existsSync45(paths.memoriesDir)) {
9923
+ const root = findProjectRoot27(opts.dir);
9924
+ const paths = resolveHaivePaths24(root);
9925
+ if (!existsSync46(paths.memoriesDir)) {
9829
9926
  ui.error(`No .ai/memories at ${root}.`);
9830
9927
  process.exitCode = 1;
9831
9928
  return;
@@ -9837,7 +9934,7 @@ function registerMemoryRm(memory2) {
9837
9934
  process.exitCode = 1;
9838
9935
  return;
9839
9936
  }
9840
- const rel = path29.relative(root, found.filePath);
9937
+ const rel = path30.relative(root, found.filePath);
9841
9938
  if (!opts.yes) {
9842
9939
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
9843
9940
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
@@ -9861,22 +9958,22 @@ function registerMemoryRm(memory2) {
9861
9958
  }
9862
9959
 
9863
9960
  // src/commands/memory-show.ts
9864
- import { existsSync as existsSync46 } from "fs";
9865
- import { readFile as readFile13 } from "fs/promises";
9866
- import path30 from "path";
9961
+ import { existsSync as existsSync47 } from "fs";
9962
+ import { readFile as readFile14 } from "fs/promises";
9963
+ import path31 from "path";
9867
9964
  import "commander";
9868
9965
  import {
9869
9966
  deriveConfidence as deriveConfidence10,
9870
- findProjectRoot as findProjectRoot27,
9967
+ findProjectRoot as findProjectRoot28,
9871
9968
  getUsage as getUsage15,
9872
9969
  loadUsageIndex as loadUsageIndex19,
9873
- resolveHaivePaths as resolveHaivePaths24
9970
+ resolveHaivePaths as resolveHaivePaths25
9874
9971
  } from "@hiveai/core";
9875
9972
  function registerMemoryShow(memory2) {
9876
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) => {
9877
- const root = findProjectRoot27(opts.dir);
9878
- const paths = resolveHaivePaths24(root);
9879
- if (!existsSync46(paths.memoriesDir)) {
9974
+ const root = findProjectRoot28(opts.dir);
9975
+ const paths = resolveHaivePaths25(root);
9976
+ if (!existsSync47(paths.memoriesDir)) {
9880
9977
  ui.error(`No .ai/memories at ${root}.`);
9881
9978
  process.exitCode = 1;
9882
9979
  return;
@@ -9889,7 +9986,7 @@ function registerMemoryShow(memory2) {
9889
9986
  return;
9890
9987
  }
9891
9988
  if (opts.raw) {
9892
- console.log(await readFile13(found.filePath, "utf8"));
9989
+ console.log(await readFile14(found.filePath, "utf8"));
9893
9990
  return;
9894
9991
  }
9895
9992
  const fm = found.memory.frontmatter;
@@ -9905,7 +10002,7 @@ function registerMemoryShow(memory2) {
9905
10002
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
9906
10003
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
9907
10004
  console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
9908
- console.log(`${ui.dim("file:")} ${path30.relative(root, found.filePath)}`);
10005
+ console.log(`${ui.dim("file:")} ${path31.relative(root, found.filePath)}`);
9909
10006
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
9910
10007
  console.log(ui.dim("anchor:"));
9911
10008
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -9920,21 +10017,21 @@ function registerMemoryShow(memory2) {
9920
10017
  }
9921
10018
 
9922
10019
  // src/commands/memory-stats.ts
9923
- import { existsSync as existsSync47 } from "fs";
9924
- import path31 from "path";
10020
+ import { existsSync as existsSync48 } from "fs";
10021
+ import path33 from "path";
9925
10022
  import "commander";
9926
10023
  import {
9927
10024
  deriveConfidence as deriveConfidence11,
9928
- findProjectRoot as findProjectRoot28,
10025
+ findProjectRoot as findProjectRoot29,
9929
10026
  getUsage as getUsage16,
9930
10027
  loadUsageIndex as loadUsageIndex20,
9931
- resolveHaivePaths as resolveHaivePaths25
10028
+ resolveHaivePaths as resolveHaivePaths26
9932
10029
  } from "@hiveai/core";
9933
10030
  function registerMemoryStats(memory2) {
9934
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) => {
9935
- const root = findProjectRoot28(opts.dir);
9936
- const paths = resolveHaivePaths25(root);
9937
- if (!existsSync47(paths.memoriesDir)) {
10032
+ const root = findProjectRoot29(opts.dir);
10033
+ const paths = resolveHaivePaths26(root);
10034
+ if (!existsSync48(paths.memoriesDir)) {
9938
10035
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9939
10036
  process.exitCode = 1;
9940
10037
  return;
@@ -9959,19 +10056,19 @@ function registerMemoryStats(memory2) {
9959
10056
  console.log(
9960
10057
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
9961
10058
  );
9962
- console.log(` ${ui.dim(path31.relative(root, filePath))}`);
10059
+ console.log(` ${ui.dim(path33.relative(root, filePath))}`);
9963
10060
  }
9964
10061
  });
9965
10062
  }
9966
10063
 
9967
10064
  // src/commands/memory-verify.ts
9968
10065
  import { writeFile as writeFile21 } from "fs/promises";
9969
- import { existsSync as existsSync48 } from "fs";
9970
- import path33 from "path";
10066
+ import { existsSync as existsSync49 } from "fs";
10067
+ import path34 from "path";
9971
10068
  import "commander";
9972
10069
  import {
9973
- findProjectRoot as findProjectRoot29,
9974
- resolveHaivePaths as resolveHaivePaths26,
10070
+ findProjectRoot as findProjectRoot30,
10071
+ resolveHaivePaths as resolveHaivePaths27,
9975
10072
  serializeMemory as serializeMemory19,
9976
10073
  verifyAnchor as verifyAnchor3
9977
10074
  } from "@hiveai/core";
@@ -9979,9 +10076,9 @@ function registerMemoryVerify(memory2) {
9979
10076
  memory2.command("verify").description(
9980
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"
9981
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) => {
9982
- const root = findProjectRoot29(opts.dir);
9983
- const paths = resolveHaivePaths26(root);
9984
- if (!existsSync48(paths.memoriesDir)) {
10079
+ const root = findProjectRoot30(opts.dir);
10080
+ const paths = resolveHaivePaths27(root);
10081
+ if (!existsSync49(paths.memoriesDir)) {
9985
10082
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
9986
10083
  process.exitCode = 1;
9987
10084
  return;
@@ -10004,7 +10101,7 @@ function registerMemoryVerify(memory2) {
10004
10101
  anchorlessIds.push(mem.frontmatter.id);
10005
10102
  continue;
10006
10103
  }
10007
- const rel = path33.relative(root, filePath);
10104
+ const rel = path34.relative(root, filePath);
10008
10105
  if (result.stale) {
10009
10106
  staleCount++;
10010
10107
  console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
@@ -10067,30 +10164,30 @@ function applyVerification2(mem, result) {
10067
10164
  }
10068
10165
 
10069
10166
  // src/commands/memory-import.ts
10070
- import { readFile as readFile14 } from "fs/promises";
10071
- import { existsSync as existsSync49 } from "fs";
10167
+ import { readFile as readFile15 } from "fs/promises";
10168
+ import { existsSync as existsSync50 } from "fs";
10072
10169
  import "commander";
10073
10170
  import {
10074
- findProjectRoot as findProjectRoot30,
10075
- resolveHaivePaths as resolveHaivePaths27
10171
+ findProjectRoot as findProjectRoot31,
10172
+ resolveHaivePaths as resolveHaivePaths28
10076
10173
  } from "@hiveai/core";
10077
10174
  function registerMemoryImport(memory2) {
10078
10175
  memory2.command("import").description(
10079
10176
  "Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
10080
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) => {
10081
- const root = findProjectRoot30(opts.dir);
10082
- const paths = resolveHaivePaths27(root);
10083
- if (!existsSync49(paths.haiveDir)) {
10178
+ const root = findProjectRoot31(opts.dir);
10179
+ const paths = resolveHaivePaths28(root);
10180
+ if (!existsSync50(paths.haiveDir)) {
10084
10181
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10085
10182
  process.exitCode = 1;
10086
10183
  return;
10087
10184
  }
10088
- if (!existsSync49(opts.from)) {
10185
+ if (!existsSync50(opts.from)) {
10089
10186
  ui.error(`File not found: ${opts.from}`);
10090
10187
  process.exitCode = 1;
10091
10188
  return;
10092
10189
  }
10093
- const content = await readFile14(opts.from, "utf8");
10190
+ const content = await readFile15(opts.from, "utf8");
10094
10191
  const scope = opts.scope ?? "team";
10095
10192
  ui.info(`Preparing import from: ${opts.from} (scope=${scope})`);
10096
10193
  ui.info(`Content length: ${content.length} chars`);
@@ -10118,14 +10215,14 @@ function registerMemoryImport(memory2) {
10118
10215
  }
10119
10216
 
10120
10217
  // src/commands/memory-import-changelog.ts
10121
- import { existsSync as existsSync50 } from "fs";
10122
- import { readFile as readFile15, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
10123
- 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";
10124
10221
  import "commander";
10125
10222
  import {
10126
10223
  buildFrontmatter as buildFrontmatter9,
10127
- findProjectRoot as findProjectRoot31,
10128
- resolveHaivePaths as resolveHaivePaths28,
10224
+ findProjectRoot as findProjectRoot32,
10225
+ resolveHaivePaths as resolveHaivePaths29,
10129
10226
  serializeMemory as serializeMemory20
10130
10227
  } from "@hiveai/core";
10131
10228
  function parseChangelog(content) {
@@ -10190,15 +10287,15 @@ function registerMemoryImportChangelog(memory2) {
10190
10287
  "--versions <csv>",
10191
10288
  "only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
10192
10289
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
10193
- const root = findProjectRoot31(opts.dir);
10194
- const paths = resolveHaivePaths28(root);
10195
- const changelogPath = path34.resolve(root, opts.fromChangelog);
10196
- 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)) {
10197
10294
  ui.error(`CHANGELOG not found: ${changelogPath}`);
10198
10295
  process.exitCode = 1;
10199
10296
  return;
10200
10297
  }
10201
- const content = await readFile15(changelogPath, "utf8");
10298
+ const content = await readFile16(changelogPath, "utf8");
10202
10299
  let entries = parseChangelog(content);
10203
10300
  if (entries.length === 0) {
10204
10301
  ui.warn("No breaking changes, deprecations, or removals found in the CHANGELOG.");
@@ -10213,9 +10310,9 @@ function registerMemoryImportChangelog(memory2) {
10213
10310
  entries = entries.filter((e) => requested.includes(e.version));
10214
10311
  }
10215
10312
  }
10216
- const pkgName = opts.package ?? path34.basename(path34.dirname(changelogPath));
10313
+ const pkgName = opts.package ?? path35.basename(path35.dirname(changelogPath));
10217
10314
  const scope = opts.scope ?? "team";
10218
- const teamDir = path34.join(paths.memoriesDir, scope);
10315
+ const teamDir = path35.join(paths.memoriesDir, scope);
10219
10316
  await mkdir14(teamDir, { recursive: true });
10220
10317
  let saved = 0;
10221
10318
  for (const entry of entries) {
@@ -10238,7 +10335,7 @@ function registerMemoryImportChangelog(memory2) {
10238
10335
  lines.push("");
10239
10336
  }
10240
10337
  lines.push(
10241
- `**Source:** \`${path34.relative(root, changelogPath)}\`
10338
+ `**Source:** \`${path35.relative(root, changelogPath)}\`
10242
10339
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
10243
10340
  );
10244
10341
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -10253,11 +10350,11 @@ function registerMemoryImportChangelog(memory2) {
10253
10350
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
10254
10351
  `v${entry.version}`
10255
10352
  ],
10256
- paths: [path34.relative(root, changelogPath)],
10353
+ paths: [path35.relative(root, changelogPath)],
10257
10354
  topic: `changelog-${pkgName}-${entry.version}`
10258
10355
  });
10259
10356
  await writeFile23(
10260
- path34.join(teamDir, `${fm.id}.md`),
10357
+ path35.join(teamDir, `${fm.id}.md`),
10261
10358
  serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
10262
10359
  "utf8"
10263
10360
  );
@@ -10280,17 +10377,17 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
10280
10377
  }
10281
10378
 
10282
10379
  // src/commands/memory-digest.ts
10283
- import { existsSync as existsSync51 } from "fs";
10380
+ import { existsSync as existsSync53 } from "fs";
10284
10381
  import { writeFile as writeFile24 } from "fs/promises";
10285
- import path35 from "path";
10382
+ import path36 from "path";
10286
10383
  import "commander";
10287
10384
  import {
10288
10385
  deriveConfidence as deriveConfidence12,
10289
- findProjectRoot as findProjectRoot32,
10386
+ findProjectRoot as findProjectRoot33,
10290
10387
  getUsage as getUsage17,
10291
10388
  loadMemoriesFromDir as loadMemoriesFromDir26,
10292
10389
  loadUsageIndex as loadUsageIndex21,
10293
- resolveHaivePaths as resolveHaivePaths29
10390
+ resolveHaivePaths as resolveHaivePaths30
10294
10391
  } from "@hiveai/core";
10295
10392
  var CONFIDENCE_EMOJI = {
10296
10393
  unverified: "\u2B1C",
@@ -10303,9 +10400,9 @@ function registerMemoryDigest(program2) {
10303
10400
  program2.command("digest").description(
10304
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"
10305
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) => {
10306
- const root = findProjectRoot32(opts.dir);
10307
- const paths = resolveHaivePaths29(root);
10308
- if (!existsSync51(paths.memoriesDir)) {
10403
+ const root = findProjectRoot33(opts.dir);
10404
+ const paths = resolveHaivePaths30(root);
10405
+ if (!existsSync53(paths.memoriesDir)) {
10309
10406
  ui.error("No .ai/memories found. Run `haive init` first.");
10310
10407
  process.exitCode = 1;
10311
10408
  return;
@@ -10377,7 +10474,7 @@ function registerMemoryDigest(program2) {
10377
10474
  );
10378
10475
  const digest = lines.join("\n");
10379
10476
  if (opts.out) {
10380
- const outPath = path35.resolve(process.cwd(), opts.out);
10477
+ const outPath = path36.resolve(process.cwd(), opts.out);
10381
10478
  await writeFile24(outPath, digest, "utf8");
10382
10479
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
10383
10480
  } else {
@@ -10387,23 +10484,23 @@ function registerMemoryDigest(program2) {
10387
10484
  }
10388
10485
 
10389
10486
  // src/commands/session-end.ts
10390
- import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile16, rm as rm2 } from "fs/promises";
10391
- 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";
10392
10489
  import { spawn as spawn4 } from "child_process";
10393
- import path36 from "path";
10490
+ import path37 from "path";
10394
10491
  import "commander";
10395
10492
  import {
10396
10493
  buildFrontmatter as buildFrontmatter10,
10397
- findProjectRoot as findProjectRoot33,
10494
+ findProjectRoot as findProjectRoot34,
10398
10495
  loadMemoriesFromDir as loadMemoriesFromDir27,
10399
10496
  memoryFilePath as memoryFilePath9,
10400
- resolveHaivePaths as resolveHaivePaths30,
10497
+ resolveHaivePaths as resolveHaivePaths31,
10401
10498
  serializeMemory as serializeMemory21
10402
10499
  } from "@hiveai/core";
10403
10500
  async function buildAutoRecap(paths) {
10404
- const obsFile = path36.join(paths.haiveDir, ".cache", "observations.jsonl");
10405
- if (!existsSync53(obsFile)) return await buildGitAutoRecap(paths);
10406
- 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(() => "");
10407
10504
  if (!raw.trim()) return await buildGitAutoRecap(paths);
10408
10505
  const lines = raw.split("\n").filter(Boolean);
10409
10506
  const obs = [];
@@ -10591,9 +10688,9 @@ function registerSessionEnd(session2) {
10591
10688
  --next "Add integration tests for webhook signature validation"
10592
10689
  `
10593
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) => {
10594
- const root = findProjectRoot33(opts.dir);
10595
- const paths = resolveHaivePaths30(root);
10596
- if (!existsSync53(paths.haiveDir)) {
10691
+ const root = findProjectRoot34(opts.dir);
10692
+ const paths = resolveHaivePaths31(root);
10693
+ if (!existsSync54(paths.haiveDir)) {
10597
10694
  if (opts.auto || opts.quiet) return;
10598
10695
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
10599
10696
  process.exitCode = 1;
@@ -10626,18 +10723,18 @@ function registerSessionEnd(session2) {
10626
10723
  });
10627
10724
  const topic = recapTopic2(scope, opts.module);
10628
10725
  const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
10629
- const missingPaths = filesTouched.filter((p) => !existsSync53(path36.resolve(root, p)));
10726
+ const missingPaths = filesTouched.filter((p) => !existsSync54(path37.resolve(root, p)));
10630
10727
  if (missingPaths.length > 0 && !opts.quiet) {
10631
10728
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
10632
10729
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
10633
10730
  }
10634
10731
  const cleanupObservations = async () => {
10635
10732
  if (!opts.auto) return;
10636
- const obsFile = path36.join(paths.haiveDir, ".cache", "observations.jsonl");
10637
- 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(() => {
10638
10735
  });
10639
10736
  };
10640
- if (existsSync53(paths.memoriesDir)) {
10737
+ if (existsSync54(paths.memoriesDir)) {
10641
10738
  const existing = await loadMemoriesFromDir27(paths.memoriesDir);
10642
10739
  const topicMatch = existing.find(
10643
10740
  ({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
@@ -10658,7 +10755,7 @@ function registerSessionEnd(session2) {
10658
10755
  await cleanupObservations();
10659
10756
  if (!opts.quiet) {
10660
10757
  ui.success(`Session recap updated (revision #${revisionCount})`);
10661
- ui.info(`id=${fm.id} file=${path36.relative(root, topicMatch.filePath)}`);
10758
+ ui.info(`id=${fm.id} file=${path37.relative(root, topicMatch.filePath)}`);
10662
10759
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
10663
10760
  }
10664
10761
  return;
@@ -10675,12 +10772,12 @@ function registerSessionEnd(session2) {
10675
10772
  status: "validated"
10676
10773
  });
10677
10774
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
10678
- await mkdir15(path36.dirname(file), { recursive: true });
10775
+ await mkdir15(path37.dirname(file), { recursive: true });
10679
10776
  await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
10680
10777
  await cleanupObservations();
10681
10778
  if (!opts.quiet) {
10682
10779
  ui.success(`Session recap created`);
10683
- 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)}`);
10684
10781
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
10685
10782
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
10686
10783
  }
@@ -10692,22 +10789,22 @@ function parseCsv5(value) {
10692
10789
  }
10693
10790
  function normalizeAnchorPath(root, filePath) {
10694
10791
  if (!filePath) return filePath;
10695
- if (!path36.isAbsolute(filePath)) return filePath;
10696
- const rel = path36.relative(root, filePath);
10792
+ if (!path37.isAbsolute(filePath)) return filePath;
10793
+ const rel = path37.relative(root, filePath);
10697
10794
  if (rel.startsWith("..")) return filePath;
10698
10795
  return rel;
10699
10796
  }
10700
10797
 
10701
10798
  // src/commands/snapshot.ts
10702
- import { existsSync as existsSync54 } from "fs";
10799
+ import { existsSync as existsSync55 } from "fs";
10703
10800
  import { readdir as readdir4 } from "fs/promises";
10704
- import path37 from "path";
10801
+ import path38 from "path";
10705
10802
  import "commander";
10706
10803
  import {
10707
10804
  diffContract,
10708
- findProjectRoot as findProjectRoot34,
10709
- loadConfig as loadConfig6,
10710
- resolveHaivePaths as resolveHaivePaths31,
10805
+ findProjectRoot as findProjectRoot35,
10806
+ loadConfig as loadConfig7,
10807
+ resolveHaivePaths as resolveHaivePaths32,
10711
10808
  snapshotContract
10712
10809
  } from "@hiveai/core";
10713
10810
  function registerSnapshot(program2) {
@@ -10732,16 +10829,16 @@ function registerSnapshot(program2) {
10732
10829
  "--format <format>",
10733
10830
  "contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
10734
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) => {
10735
- const root = findProjectRoot34(opts.dir);
10736
- const paths = resolveHaivePaths31(root);
10737
- if (!existsSync54(paths.haiveDir)) {
10832
+ const root = findProjectRoot35(opts.dir);
10833
+ const paths = resolveHaivePaths32(root);
10834
+ if (!existsSync55(paths.haiveDir)) {
10738
10835
  ui.error("No .ai/ found. Run `haive init` first.");
10739
10836
  process.exitCode = 1;
10740
10837
  return;
10741
10838
  }
10742
10839
  if (opts.list) {
10743
- const contractsDir = path37.join(paths.haiveDir, "contracts");
10744
- if (!existsSync54(contractsDir)) {
10840
+ const contractsDir = path38.join(paths.haiveDir, "contracts");
10841
+ if (!existsSync55(contractsDir)) {
10745
10842
  console.log(ui.dim("No contract snapshots found."));
10746
10843
  return;
10747
10844
  }
@@ -10761,7 +10858,7 @@ function registerSnapshot(program2) {
10761
10858
  }
10762
10859
  if (opts.diff) {
10763
10860
  if (!opts.name) {
10764
- const config2 = await loadConfig6(paths);
10861
+ const config2 = await loadConfig7(paths);
10765
10862
  const contracts = config2.contractFiles ?? [];
10766
10863
  if (contracts.length === 0) {
10767
10864
  ui.error("--diff requires --name, or configure contractFiles in haive.config.json");
@@ -10773,7 +10870,7 @@ function registerSnapshot(program2) {
10773
10870
  }
10774
10871
  return;
10775
10872
  }
10776
- const config = await loadConfig6(paths);
10873
+ const config = await loadConfig7(paths);
10777
10874
  const configured = (config.contractFiles ?? []).find((c) => c.name === opts.name);
10778
10875
  if (!configured && !opts.contract) {
10779
10876
  ui.error(
@@ -10796,7 +10893,7 @@ function registerSnapshot(program2) {
10796
10893
  return;
10797
10894
  }
10798
10895
  const contractPath = opts.contract;
10799
- const name = opts.name ?? path37.basename(contractPath, path37.extname(contractPath));
10896
+ const name = opts.name ?? path38.basename(contractPath, path38.extname(contractPath));
10800
10897
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
10801
10898
  const contract = { name, path: contractPath, format };
10802
10899
  try {
@@ -10851,8 +10948,8 @@ async function runDiff(root, haiveDir, contract) {
10851
10948
  }
10852
10949
  }
10853
10950
  function detectFormat(filePath) {
10854
- const ext = path37.extname(filePath).toLowerCase();
10855
- const base = path37.basename(filePath).toLowerCase();
10951
+ const ext = path38.extname(filePath).toLowerCase();
10952
+ const base = path38.basename(filePath).toLowerCase();
10856
10953
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
10857
10954
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
10858
10955
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -10865,16 +10962,16 @@ function detectFormat(filePath) {
10865
10962
  }
10866
10963
 
10867
10964
  // src/commands/hub.ts
10868
- import { existsSync as existsSync55 } from "fs";
10869
- import { mkdir as mkdir16, readFile as readFile17, writeFile as writeFile26, copyFile } from "fs/promises";
10870
- 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";
10871
10968
  import { spawnSync as spawnSync5 } from "child_process";
10872
10969
  import "commander";
10873
10970
  import {
10874
- findProjectRoot as findProjectRoot35,
10875
- loadConfig as loadConfig7,
10971
+ findProjectRoot as findProjectRoot36,
10972
+ loadConfig as loadConfig8,
10876
10973
  loadMemoriesFromDir as loadMemoriesFromDir28,
10877
- resolveHaivePaths as resolveHaivePaths32,
10974
+ resolveHaivePaths as resolveHaivePaths33,
10878
10975
  saveConfig as saveConfig3,
10879
10976
  serializeMemory as serializeMemory23
10880
10977
  } from "@hiveai/core";
@@ -10886,7 +10983,7 @@ function registerHub(program2) {
10886
10983
  hub.command("init <hubPath>").description(
10887
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"
10888
10985
  ).action(async (hubPath) => {
10889
- const absPath = path38.resolve(hubPath);
10986
+ const absPath = path39.resolve(hubPath);
10890
10987
  await mkdir16(absPath, { recursive: true });
10891
10988
  const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
10892
10989
  if (gitCheck.status !== 0) {
@@ -10897,10 +10994,10 @@ function registerHub(program2) {
10897
10994
  return;
10898
10995
  }
10899
10996
  }
10900
- const sharedDir = path38.join(absPath, ".ai", "memories", "shared");
10997
+ const sharedDir = path39.join(absPath, ".ai", "memories", "shared");
10901
10998
  await mkdir16(sharedDir, { recursive: true });
10902
10999
  await writeFile26(
10903
- path38.join(absPath, ".ai", "README.md"),
11000
+ path39.join(absPath, ".ai", "README.md"),
10904
11001
  `# hAIve Team Knowledge Hub
10905
11002
 
10906
11003
  This repo is a shared knowledge hub for hAIve.
@@ -10922,7 +11019,7 @@ haive hub pull # import into a project
10922
11019
  "utf8"
10923
11020
  );
10924
11021
  await writeFile26(
10925
- path38.join(absPath, ".gitignore"),
11022
+ path39.join(absPath, ".gitignore"),
10926
11023
  ".ai/.cache/\n.ai/memories/personal/\n",
10927
11024
  "utf8"
10928
11025
  );
@@ -10937,7 +11034,7 @@ haive hub pull # import into a project
10937
11034
  `
10938
11035
  Next steps:
10939
11036
  1. Add hubPath to your project's .ai/haive.config.json:
10940
- { "hubPath": "${path38.relative(process.cwd(), absPath)}" }
11037
+ { "hubPath": "${path39.relative(process.cwd(), absPath)}" }
10941
11038
  2. Run \`haive hub push\` to publish your shared memories
10942
11039
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
10943
11040
  `
@@ -10956,9 +11053,9 @@ Next steps:
10956
11053
  haive hub push --commit --message "feat: add payment API contract memories"
10957
11054
  `
10958
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) => {
10959
- const root = findProjectRoot35(opts.dir);
10960
- const paths = resolveHaivePaths32(root);
10961
- const config = await loadConfig7(paths);
11056
+ const root = findProjectRoot36(opts.dir);
11057
+ const paths = resolveHaivePaths33(root);
11058
+ const config = await loadConfig8(paths);
10962
11059
  if (!config.hubPath) {
10963
11060
  ui.error(
10964
11061
  'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
@@ -10966,14 +11063,14 @@ Next steps:
10966
11063
  process.exitCode = 1;
10967
11064
  return;
10968
11065
  }
10969
- const hubRoot = path38.resolve(root, config.hubPath);
10970
- if (!existsSync55(hubRoot)) {
11066
+ const hubRoot = path39.resolve(root, config.hubPath);
11067
+ if (!existsSync56(hubRoot)) {
10971
11068
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
10972
11069
  process.exitCode = 1;
10973
11070
  return;
10974
11071
  }
10975
- const projectName = path38.basename(root);
10976
- 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);
10977
11074
  await mkdir16(destDir, { recursive: true });
10978
11075
  const all = await loadMemoriesFromDir28(paths.memoriesDir);
10979
11076
  const shared = all.filter(
@@ -10992,7 +11089,7 @@ Next steps:
10992
11089
  for (const { memory: memory2 } of shared) {
10993
11090
  const fm = memory2.frontmatter;
10994
11091
  const fileName = `${fm.id}.md`;
10995
- const destPath = path38.join(destDir, fileName);
11092
+ const destPath = path39.join(destDir, fileName);
10996
11093
  await writeFile26(destPath, serializeMemory23(memory2), "utf8");
10997
11094
  pushed++;
10998
11095
  }
@@ -11000,7 +11097,7 @@ Next steps:
11000
11097
  console.log(ui.dim(` Location: ${destDir}`));
11001
11098
  if (opts.commit) {
11002
11099
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
11003
- spawnSync5("git", ["add", path38.join(".ai", "memories", "shared", projectName)], {
11100
+ spawnSync5("git", ["add", path39.join(".ai", "memories", "shared", projectName)], {
11004
11101
  cwd: hubRoot
11005
11102
  });
11006
11103
  const commit = spawnSync5("git", ["commit", "-m", message], {
@@ -11025,9 +11122,9 @@ Next steps:
11025
11122
  hub.command("pull").description(
11026
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"
11027
11124
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
11028
- const root = findProjectRoot35(opts.dir);
11029
- const paths = resolveHaivePaths32(root);
11030
- const config = await loadConfig7(paths);
11125
+ const root = findProjectRoot36(opts.dir);
11126
+ const paths = resolveHaivePaths33(root);
11127
+ const config = await loadConfig8(paths);
11031
11128
  if (!config.hubPath) {
11032
11129
  ui.error(
11033
11130
  'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
@@ -11035,13 +11132,13 @@ Next steps:
11035
11132
  process.exitCode = 1;
11036
11133
  return;
11037
11134
  }
11038
- const hubRoot = path38.resolve(root, config.hubPath);
11039
- const hubSharedDir = path38.join(hubRoot, ".ai", "memories", "shared");
11040
- if (!existsSync55(hubSharedDir)) {
11135
+ const hubRoot = path39.resolve(root, config.hubPath);
11136
+ const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
11137
+ if (!existsSync56(hubSharedDir)) {
11041
11138
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
11042
11139
  return;
11043
11140
  }
11044
- const projectName = path38.basename(root);
11141
+ const projectName = path39.basename(root);
11045
11142
  const { readdir: readdir7 } = await import("fs/promises");
11046
11143
  const projectDirs = (await readdir7(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
11047
11144
  if (projectDirs.length === 0) {
@@ -11051,17 +11148,17 @@ Next steps:
11051
11148
  let totalImported = 0;
11052
11149
  let totalUpdated = 0;
11053
11150
  for (const sourceName of projectDirs) {
11054
- const sourceDir = path38.join(hubSharedDir, sourceName);
11055
- const destDir = path38.join(paths.memoriesDir, "shared", sourceName);
11151
+ const sourceDir = path39.join(hubSharedDir, sourceName);
11152
+ const destDir = path39.join(paths.memoriesDir, "shared", sourceName);
11056
11153
  await mkdir16(destDir, { recursive: true });
11057
11154
  const sourceFiles = (await readdir7(sourceDir)).filter((f) => f.endsWith(".md"));
11058
11155
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
11059
11156
  const existingInDest = await loadDir(destDir);
11060
11157
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
11061
11158
  for (const file of sourceFiles) {
11062
- const srcPath = path38.join(sourceDir, file);
11063
- const destPath = path38.join(destDir, file);
11064
- 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");
11065
11162
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
11066
11163
  if (!alreadyTagged) {
11067
11164
  await copyFile(srcPath, destPath);
@@ -11084,21 +11181,21 @@ Next steps:
11084
11181
  );
11085
11182
  });
11086
11183
  hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
11087
- const root = findProjectRoot35(opts.dir);
11088
- const paths = resolveHaivePaths32(root);
11089
- const config = await loadConfig7(paths);
11184
+ const root = findProjectRoot36(opts.dir);
11185
+ const paths = resolveHaivePaths33(root);
11186
+ const config = await loadConfig8(paths);
11090
11187
  console.log(ui.bold("Hub status"));
11091
11188
  console.log(
11092
11189
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
11093
11190
  );
11094
- const sharedDir = path38.join(paths.memoriesDir, "shared");
11095
- if (existsSync55(sharedDir)) {
11191
+ const sharedDir = path39.join(paths.memoriesDir, "shared");
11192
+ if (existsSync56(sharedDir)) {
11096
11193
  const { readdir: readdir7 } = await import("fs/promises");
11097
11194
  const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
11098
11195
  console.log(`
11099
11196
  Imported from ${sources.length} source(s):`);
11100
11197
  for (const src of sources) {
11101
- 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"));
11102
11199
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
11103
11200
  }
11104
11201
  } else {
@@ -11113,7 +11210,7 @@ Next steps:
11113
11210
  if (outgoing.length > 0) {
11114
11211
  console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
11115
11212
  }
11116
- void readFile17;
11213
+ void readFile18;
11117
11214
  void writeFile26;
11118
11215
  void saveConfig3;
11119
11216
  });
@@ -11121,17 +11218,17 @@ Next steps:
11121
11218
 
11122
11219
  // src/commands/stats.ts
11123
11220
  import "commander";
11124
- import { existsSync as existsSync56 } from "fs";
11221
+ import { existsSync as existsSync57 } from "fs";
11125
11222
  import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
11126
- import path39 from "path";
11223
+ import path40 from "path";
11127
11224
  import {
11128
11225
  aggregateUsage,
11129
- findProjectRoot as findProjectRoot36,
11226
+ findProjectRoot as findProjectRoot37,
11130
11227
  loadMemoriesFromDir as loadMemoriesFromDir29,
11131
11228
  loadUsageIndex as loadUsageIndex23,
11132
11229
  parseSince,
11133
11230
  readUsageEvents as readUsageEvents2,
11134
- resolveHaivePaths as resolveHaivePaths33,
11231
+ resolveHaivePaths as resolveHaivePaths34,
11135
11232
  usageLogSize
11136
11233
  } from "@hiveai/core";
11137
11234
  function registerStats(program2) {
@@ -11140,8 +11237,8 @@ function registerStats(program2) {
11140
11237
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
11141
11238
  void 0
11142
11239
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
11143
- const root = findProjectRoot36(opts.dir);
11144
- const paths = resolveHaivePaths33(root);
11240
+ const root = findProjectRoot37(opts.dir);
11241
+ const paths = resolveHaivePaths34(root);
11145
11242
  if (opts.exportReport) {
11146
11243
  await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
11147
11244
  return;
@@ -11195,11 +11292,11 @@ function registerStats(program2) {
11195
11292
  });
11196
11293
  }
11197
11294
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11198
- 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);
11199
11296
  const size = await usageLogSize(paths);
11200
11297
  let events = await readUsageEvents2(paths);
11201
11298
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
11202
- if (existsSync56(paths.memoriesDir)) {
11299
+ if (existsSync57(paths.memoriesDir)) {
11203
11300
  const mems = await loadMemoriesFromDir29(paths.memoriesDir);
11204
11301
  for (const { memory: memory2 } of mems) {
11205
11302
  const fm = memory2.frontmatter;
@@ -11230,7 +11327,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
11230
11327
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
11231
11328
  events = [];
11232
11329
  }
11233
- await mkdir17(path39.dirname(outAbs), { recursive: true });
11330
+ await mkdir17(path40.dirname(outAbs), { recursive: true });
11234
11331
  const payload = {
11235
11332
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
11236
11333
  project_root: root,
@@ -11294,13 +11391,13 @@ import { performance } from "perf_hooks";
11294
11391
  import "commander";
11295
11392
  import {
11296
11393
  estimateTokens as estimateTokens3,
11297
- findProjectRoot as findProjectRoot37,
11298
- resolveHaivePaths as resolveHaivePaths34
11394
+ findProjectRoot as findProjectRoot38,
11395
+ resolveHaivePaths as resolveHaivePaths35
11299
11396
  } from "@hiveai/core";
11300
11397
  function registerBench(program2) {
11301
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) => {
11302
- const root = findProjectRoot37(opts.dir);
11303
- const paths = resolveHaivePaths34(root);
11399
+ const root = findProjectRoot38(opts.dir);
11400
+ const paths = resolveHaivePaths35(root);
11304
11401
  const ctx = { paths };
11305
11402
  const task = opts.task ?? "audit dependencies for security risks";
11306
11403
  const scenarios = [
@@ -11419,11 +11516,11 @@ function summarize(name, t0, payload, notes) {
11419
11516
  }
11420
11517
 
11421
11518
  // src/commands/benchmark.ts
11422
- import { existsSync as existsSync57 } from "fs";
11423
- import { readdir as readdir5, readFile as readFile18, writeFile as writeFile28 } from "fs/promises";
11424
- 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";
11425
11522
  import "commander";
11426
- import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot38 } from "@hiveai/core";
11523
+ import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot39 } from "@hiveai/core";
11427
11524
  function registerBenchmark(program2) {
11428
11525
  const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
11429
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) => {
@@ -11436,9 +11533,9 @@ function registerBenchmark(program2) {
11436
11533
  }
11437
11534
  const markdown = renderMarkdown(root, summary, rows);
11438
11535
  if (opts.out) {
11439
- 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);
11440
11537
  await writeFile28(outFile, markdown, "utf8");
11441
- ui.success(`wrote ${path40.relative(process.cwd(), outFile)}`);
11538
+ ui.success(`wrote ${path41.relative(process.cwd(), outFile)}`);
11442
11539
  return;
11443
11540
  }
11444
11541
  console.log(markdown);
@@ -11462,20 +11559,20 @@ function registerBenchmark(program2) {
11462
11559
  }
11463
11560
  function resolveBenchmarkRoot(dir) {
11464
11561
  const candidate = dir ?? "benchmarks/agent-benchmark";
11465
- if (path40.isAbsolute(candidate)) return candidate;
11466
- const projectRoot = findProjectRoot38(process.cwd());
11467
- return path40.join(projectRoot, candidate);
11562
+ if (path41.isAbsolute(candidate)) return candidate;
11563
+ const projectRoot = findProjectRoot39(process.cwd());
11564
+ return path41.join(projectRoot, candidate);
11468
11565
  }
11469
11566
  async function collectRows(root) {
11470
- if (!existsSync57(root)) throw new Error(`Benchmark directory not found: ${root}`);
11567
+ if (!existsSync58(root)) throw new Error(`Benchmark directory not found: ${root}`);
11471
11568
  const entries = await readdir5(root, { withFileTypes: true });
11472
11569
  const rows = [];
11473
11570
  for (const entry of entries) {
11474
11571
  if (!entry.isDirectory()) continue;
11475
- const fixtureDir = path40.join(root, entry.name);
11476
- const reportFile = path40.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
11477
- if (!existsSync57(reportFile)) continue;
11478
- 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");
11479
11576
  rows.push(parseAgentReport(entry.name, report));
11480
11577
  }
11481
11578
  return rows.sort((a, b) => a.fixture.localeCompare(b.fixture));
@@ -11565,19 +11662,19 @@ function escapeRegExp(value) {
11565
11662
 
11566
11663
  // src/commands/memory-suggest.ts
11567
11664
  import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
11568
- import { existsSync as existsSync58 } from "fs";
11569
- import path41 from "path";
11665
+ import { existsSync as existsSync59 } from "fs";
11666
+ import path43 from "path";
11570
11667
  import "commander";
11571
11668
  import {
11572
11669
  aggregateUsage as aggregateUsage2,
11573
11670
  buildFrontmatter as buildFrontmatter11,
11574
- findProjectRoot as findProjectRoot39,
11575
- loadConfig as loadConfig8,
11671
+ findProjectRoot as findProjectRoot40,
11672
+ loadConfig as loadConfig9,
11576
11673
  loadMemoriesFromDir as loadMemoriesFromDir30,
11577
11674
  memoryFilePath as memoryFilePath10,
11578
11675
  parseSince as parseSince2,
11579
11676
  readUsageEvents as readUsageEvents3,
11580
- resolveHaivePaths as resolveHaivePaths35,
11677
+ resolveHaivePaths as resolveHaivePaths36,
11581
11678
  serializeMemory as serializeMemory24
11582
11679
  } from "@hiveai/core";
11583
11680
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
@@ -11594,8 +11691,8 @@ function registerMemorySuggest(memory2) {
11594
11691
  memory2.command("suggest").description(
11595
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."
11596
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) => {
11597
- const root = findProjectRoot39(opts.dir);
11598
- const paths = resolveHaivePaths35(root);
11694
+ const root = findProjectRoot40(opts.dir);
11695
+ const paths = resolveHaivePaths36(root);
11599
11696
  const events = await readUsageEvents3(paths);
11600
11697
  if (events.length === 0) {
11601
11698
  if (opts.json) {
@@ -11633,7 +11730,7 @@ function registerMemorySuggest(memory2) {
11633
11730
  inferred_type: inferType(v.tools, query)
11634
11731
  })).sort((a, b) => b.count - a.count);
11635
11732
  if (opts.autoSave) {
11636
- const config = await loadConfig8(paths);
11733
+ const config = await loadConfig9(paths);
11637
11734
  const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
11638
11735
  const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
11639
11736
  const status = config.defaultStatus === "validated" ? "validated" : "draft";
@@ -11644,7 +11741,7 @@ function registerMemorySuggest(memory2) {
11644
11741
  }
11645
11742
  const created = [];
11646
11743
  const skipped = [];
11647
- const existing = existsSync58(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
11744
+ const existing = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
11648
11745
  for (const s of top) {
11649
11746
  const slug = slugify2(s.query);
11650
11747
  if (!slug) {
@@ -11667,13 +11764,13 @@ function registerMemorySuggest(memory2) {
11667
11764
  });
11668
11765
  const body = renderTemplate(s, fm.id, status);
11669
11766
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
11670
- await mkdir18(path41.dirname(file), { recursive: true });
11671
- if (existsSync58(file)) {
11672
- 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)}` });
11673
11770
  continue;
11674
11771
  }
11675
11772
  await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
11676
- 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 });
11677
11774
  }
11678
11775
  if (opts.json) {
11679
11776
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -11771,16 +11868,16 @@ function truncate2(text, max) {
11771
11868
  }
11772
11869
 
11773
11870
  // src/commands/memory-archive.ts
11774
- import { existsSync as existsSync59 } from "fs";
11871
+ import { existsSync as existsSync60 } from "fs";
11775
11872
  import { writeFile as writeFile30 } from "fs/promises";
11776
- import path43 from "path";
11873
+ import path44 from "path";
11777
11874
  import "commander";
11778
11875
  import {
11779
- findProjectRoot as findProjectRoot40,
11876
+ findProjectRoot as findProjectRoot41,
11780
11877
  getUsage as getUsage18,
11781
11878
  loadMemoriesFromDir as loadMemoriesFromDir31,
11782
11879
  loadUsageIndex as loadUsageIndex24,
11783
- resolveHaivePaths as resolveHaivePaths36,
11880
+ resolveHaivePaths as resolveHaivePaths37,
11784
11881
  serializeMemory as serializeMemory25
11785
11882
  } from "@hiveai/core";
11786
11883
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
@@ -11788,9 +11885,9 @@ function registerMemoryArchive(memory2) {
11788
11885
  memory2.command("archive").description(
11789
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."
11790
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) => {
11791
- const root = findProjectRoot40(opts.dir);
11792
- const paths = resolveHaivePaths36(root);
11793
- if (!existsSync59(paths.memoriesDir)) {
11888
+ const root = findProjectRoot41(opts.dir);
11889
+ const paths = resolveHaivePaths37(root);
11890
+ if (!existsSync60(paths.memoriesDir)) {
11794
11891
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
11795
11892
  process.exitCode = 1;
11796
11893
  return;
@@ -11811,7 +11908,7 @@ function registerMemoryArchive(memory2) {
11811
11908
  if (typeFilter && fm.type !== typeFilter) continue;
11812
11909
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
11813
11910
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
11814
- 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)));
11815
11912
  const isAnchorless = !hasAnyAnchor;
11816
11913
  if (!isAnchorless && !allPathsGone) continue;
11817
11914
  const u = getUsage18(usage, fm.id);
@@ -11885,33 +11982,33 @@ function parseDays(input) {
11885
11982
  }
11886
11983
 
11887
11984
  // src/commands/doctor.ts
11888
- import { existsSync as existsSync60, statSync } from "fs";
11889
- import { readFile as readFile19, stat, writeFile as writeFile31 } from "fs/promises";
11890
- 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";
11891
11988
  import { execFileSync, execSync as execSync3 } from "child_process";
11892
11989
  import "commander";
11893
11990
  import {
11894
11991
  codeMapPath as codeMapPath2,
11895
- findProjectRoot as findProjectRoot41,
11992
+ findProjectRoot as findProjectRoot42,
11896
11993
  getUsage as getUsage19,
11897
11994
  loadCodeMap as loadCodeMap6,
11898
- loadConfig as loadConfig9,
11995
+ loadConfig as loadConfig10,
11899
11996
  loadMemoriesFromDir as loadMemoriesFromDir33,
11900
11997
  loadUsageIndex as loadUsageIndex25,
11901
11998
  readUsageEvents as readUsageEvents4,
11902
- resolveHaivePaths as resolveHaivePaths37
11999
+ resolveHaivePaths as resolveHaivePaths38
11903
12000
  } from "@hiveai/core";
11904
12001
  var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
11905
12002
  function registerDoctor(program2) {
11906
12003
  program2.command("doctor").description(
11907
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."
11908
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) => {
11909
- const root = findProjectRoot41(opts.dir);
11910
- const paths = resolveHaivePaths37(root);
12006
+ const root = findProjectRoot42(opts.dir);
12007
+ const paths = resolveHaivePaths38(root);
11911
12008
  const findings = [];
11912
12009
  const repairs = [];
11913
- const config = await loadConfig9(paths);
11914
- if (!existsSync60(paths.haiveDir)) {
12010
+ const config = await loadConfig10(paths);
12011
+ if (!existsSync61(paths.haiveDir)) {
11915
12012
  findings.push({
11916
12013
  severity: "error",
11917
12014
  code: "not-initialized",
@@ -11932,7 +12029,7 @@ function registerDoctor(program2) {
11932
12029
  })
11933
12030
  );
11934
12031
  }
11935
- if (!existsSync60(paths.projectContext)) {
12032
+ if (!existsSync61(paths.projectContext)) {
11936
12033
  findings.push({
11937
12034
  severity: "warn",
11938
12035
  code: "no-project-context",
@@ -11940,8 +12037,8 @@ function registerDoctor(program2) {
11940
12037
  fix: "haive init"
11941
12038
  });
11942
12039
  } else {
11943
- const { readFile: readFile21 } = await import("fs/promises");
11944
- const content = await readFile21(paths.projectContext, "utf8");
12040
+ const { readFile: readFile23 } = await import("fs/promises");
12041
+ const content = await readFile23(paths.projectContext, "utf8");
11945
12042
  const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `haive init`");
11946
12043
  if (isTemplate) {
11947
12044
  findings.push({
@@ -11961,7 +12058,7 @@ function registerDoctor(program2) {
11961
12058
  });
11962
12059
  }
11963
12060
  }
11964
- const memories = existsSync60(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
12061
+ const memories = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
11965
12062
  const now = Date.now();
11966
12063
  if (memories.length === 0) {
11967
12064
  findings.push({
@@ -12102,12 +12199,12 @@ function registerDoctor(program2) {
12102
12199
  }
12103
12200
  }
12104
12201
  if (config.enforcement?.requireBriefingFirst) {
12105
- const claudeSettings = path44.join(root, ".claude", "settings.local.json");
12202
+ const claudeSettings = path45.join(root, ".claude", "settings.local.json");
12106
12203
  let hasClaudeEnforcement = false;
12107
- if (existsSync60(claudeSettings)) {
12204
+ if (existsSync61(claudeSettings)) {
12108
12205
  try {
12109
- const { readFile: readFile21 } = await import("fs/promises");
12110
- const raw = await readFile21(claudeSettings, "utf8");
12206
+ const { readFile: readFile23 } = await import("fs/promises");
12207
+ const raw = await readFile23(claudeSettings, "utf8");
12111
12208
  hasClaudeEnforcement = raw.includes("haive enforce session-start") && raw.includes("haive enforce pre-tool-use");
12112
12209
  } catch {
12113
12210
  hasClaudeEnforcement = false;
@@ -12130,14 +12227,14 @@ function registerDoctor(program2) {
12130
12227
  fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
12131
12228
  });
12132
12229
  }
12133
- findings.push(...await collectInstallFindings(root, "0.9.28"));
12230
+ findings.push(...await collectInstallFindings(root, "0.9.29"));
12134
12231
  try {
12135
12232
  const legacyRaw = execSync3("haive-mcp --version", {
12136
12233
  encoding: "utf8",
12137
12234
  timeout: 3e3,
12138
12235
  stdio: ["ignore", "pipe", "ignore"]
12139
12236
  }).trim();
12140
- const cliVersion = "0.9.28";
12237
+ const cliVersion = "0.9.29";
12141
12238
  if (legacyRaw && legacyRaw !== cliVersion) {
12142
12239
  findings.push({
12143
12240
  severity: "warn",
@@ -12153,17 +12250,17 @@ npm uninstall -g @hiveai/mcp`
12153
12250
  }
12154
12251
  {
12155
12252
  const configPaths = [
12156
- path44.join(root, ".mcp.json"),
12157
- path44.join(root, ".cursor", "mcp.json"),
12158
- 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")
12159
12256
  ];
12160
12257
  const staleConfigs = [];
12161
12258
  for (const cfgPath of configPaths) {
12162
- if (!existsSync60(cfgPath)) continue;
12259
+ if (!existsSync61(cfgPath)) continue;
12163
12260
  try {
12164
- const raw = await readFile19(cfgPath, "utf8");
12261
+ const raw = await readFile20(cfgPath, "utf8");
12165
12262
  if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
12166
- staleConfigs.push(path44.relative(root, cfgPath));
12263
+ staleConfigs.push(path45.relative(root, cfgPath));
12167
12264
  if (opts.fix && !opts.dryRun) {
12168
12265
  const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
12169
12266
  await writeFile31(cfgPath, updated, "utf8");
@@ -12453,9 +12550,9 @@ which -a haive`
12453
12550
  ".vscode/mcp.json"
12454
12551
  ];
12455
12552
  for (const rel of integrationFiles) {
12456
- const file = path44.join(root, rel);
12457
- if (!existsSync60(file)) continue;
12458
- 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(() => "");
12459
12556
  for (const bin of extractAbsoluteHaiveBins(text)) {
12460
12557
  const version = versionForBinary(bin);
12461
12558
  if (!version) {
@@ -12479,7 +12576,7 @@ which -a haive`
12479
12576
  }
12480
12577
  async function collectWorkspaceVersionFindings(root, expectedVersion) {
12481
12578
  const findings = [];
12482
- const rootPkg = await readJson(path44.join(root, "package.json"));
12579
+ const rootPkg = await readJson(path45.join(root, "package.json"));
12483
12580
  const workspacePackages = [
12484
12581
  "packages/core/package.json",
12485
12582
  "packages/embeddings/package.json",
@@ -12488,7 +12585,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
12488
12585
  ];
12489
12586
  const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
12490
12587
  rel,
12491
- pkg: await readJson(path44.join(root, rel))
12588
+ pkg: await readJson(path45.join(root, rel))
12492
12589
  })))).filter((item) => item.pkg);
12493
12590
  const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
12494
12591
  if (!isHaiveWorkspace) return findings;
@@ -12550,9 +12647,9 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
12550
12647
  }
12551
12648
  }
12552
12649
  async function readJson(file) {
12553
- if (!existsSync60(file)) return null;
12650
+ if (!existsSync61(file)) return null;
12554
12651
  try {
12555
- return JSON.parse(await readFile19(file, "utf8"));
12652
+ return JSON.parse(await readFile20(file, "utf8"));
12556
12653
  } catch {
12557
12654
  return null;
12558
12655
  }
@@ -12597,22 +12694,22 @@ function extractAbsoluteHaiveBins(text) {
12597
12694
  }
12598
12695
 
12599
12696
  // src/commands/playback.ts
12600
- import { existsSync as existsSync61 } from "fs";
12697
+ import { existsSync as existsSync63 } from "fs";
12601
12698
  import "commander";
12602
12699
  import {
12603
- findProjectRoot as findProjectRoot42,
12700
+ findProjectRoot as findProjectRoot43,
12604
12701
  loadMemoriesFromDir as loadMemoriesFromDir34,
12605
12702
  parseSince as parseSince3,
12606
12703
  readUsageEvents as readUsageEvents5,
12607
- resolveHaivePaths as resolveHaivePaths38
12704
+ resolveHaivePaths as resolveHaivePaths39
12608
12705
  } from "@hiveai/core";
12609
12706
  var MS_PER_MINUTE = 6e4;
12610
12707
  function registerPlayback(program2) {
12611
12708
  program2.command("playback").description(
12612
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?'"
12613
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) => {
12614
- const root = findProjectRoot42(opts.dir);
12615
- const paths = resolveHaivePaths38(root);
12711
+ const root = findProjectRoot43(opts.dir);
12712
+ const paths = resolveHaivePaths39(root);
12616
12713
  const events = await readUsageEvents5(paths);
12617
12714
  if (events.length === 0) {
12618
12715
  if (opts.json) {
@@ -12627,7 +12724,7 @@ function registerPlayback(program2) {
12627
12724
  const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
12628
12725
  const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
12629
12726
  const sessions = bucketSessions(filtered, gapMs);
12630
- const all = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
12727
+ const all = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
12631
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);
12632
12729
  const enriched = sessions.map((s, i) => {
12633
12730
  const startMs = Date.parse(s.start);
@@ -12717,8 +12814,8 @@ function truncate3(text, max) {
12717
12814
  import { spawn as spawn5 } from "child_process";
12718
12815
  import "commander";
12719
12816
  import {
12720
- findProjectRoot as findProjectRoot43,
12721
- resolveHaivePaths as resolveHaivePaths39
12817
+ findProjectRoot as findProjectRoot44,
12818
+ resolveHaivePaths as resolveHaivePaths40
12722
12819
  } from "@hiveai/core";
12723
12820
  function registerPrecommit(program2) {
12724
12821
  program2.command("precommit").description(
@@ -12728,8 +12825,8 @@ function registerPrecommit(program2) {
12728
12825
  "'any' | 'high-confidence' (default) | 'never' (report only)",
12729
12826
  "high-confidence"
12730
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) => {
12731
- const root = findProjectRoot43(opts.dir);
12732
- const paths = resolveHaivePaths39(root);
12828
+ const root = findProjectRoot44(opts.dir);
12829
+ const paths = resolveHaivePaths40(root);
12733
12830
  const ctx = { paths };
12734
12831
  let diff = "";
12735
12832
  let touchedPaths = opts.paths ?? [];
@@ -12859,12 +12956,12 @@ function runCommand3(cmd, args, cwd) {
12859
12956
  }
12860
12957
 
12861
12958
  // src/commands/welcome.ts
12862
- import { existsSync as existsSync63 } from "fs";
12959
+ import { existsSync as existsSync64 } from "fs";
12863
12960
  import "commander";
12864
12961
  import {
12865
- findProjectRoot as findProjectRoot44,
12962
+ findProjectRoot as findProjectRoot45,
12866
12963
  loadMemoriesFromDir as loadMemoriesFromDir35,
12867
- resolveHaivePaths as resolveHaivePaths40
12964
+ resolveHaivePaths as resolveHaivePaths41
12868
12965
  } from "@hiveai/core";
12869
12966
  var TYPE_RANK = {
12870
12967
  skill: 0,
@@ -12879,9 +12976,9 @@ function registerWelcome(program2) {
12879
12976
  program2.command("welcome").description(
12880
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"
12881
12978
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
12882
- const root = findProjectRoot44(opts.dir);
12883
- const paths = resolveHaivePaths40(root);
12884
- if (!existsSync63(paths.memoriesDir)) {
12979
+ const root = findProjectRoot45(opts.dir);
12980
+ const paths = resolveHaivePaths41(root);
12981
+ if (!existsSync64(paths.memoriesDir)) {
12885
12982
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
12886
12983
  process.exitCode = 1;
12887
12984
  return;
@@ -12940,27 +13037,27 @@ function registerMemorySuggestTopic(memory2) {
12940
13037
  }
12941
13038
 
12942
13039
  // src/commands/resolve-project.ts
12943
- import path45 from "path";
13040
+ import path46 from "path";
12944
13041
  import "commander";
12945
13042
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
12946
13043
  function registerResolveProject(program2) {
12947
13044
  program2.command("resolve-project").description(
12948
13045
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
12949
13046
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
12950
- const info = resolveProjectInfo2({ cwd: path45.resolve(opts.dir) });
13047
+ const info = resolveProjectInfo2({ cwd: path46.resolve(opts.dir) });
12951
13048
  console.log(JSON.stringify({ ok: true, info }, null, 2));
12952
13049
  });
12953
13050
  }
12954
13051
 
12955
13052
  // src/commands/runtime-journal.ts
12956
- import { existsSync as existsSync64 } from "fs";
12957
- import path46 from "path";
13053
+ import { existsSync as existsSync65 } from "fs";
13054
+ import path47 from "path";
12958
13055
  import "commander";
12959
13056
  import {
12960
13057
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
12961
- findProjectRoot as findProjectRoot45,
13058
+ findProjectRoot as findProjectRoot46,
12962
13059
  readRuntimeJournalTail as readRuntimeJournalTail2,
12963
- resolveHaivePaths as resolveHaivePaths41
13060
+ resolveHaivePaths as resolveHaivePaths42
12964
13061
  } from "@hiveai/core";
12965
13062
  function registerRuntime(program2) {
12966
13063
  const runtime = program2.command("runtime").description(
@@ -12968,18 +13065,18 @@ function registerRuntime(program2) {
12968
13065
  );
12969
13066
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
12970
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) => {
12971
- const root = path46.resolve(opts.dir ?? process.cwd());
12972
- const paths = resolveHaivePaths41(findProjectRoot45(root));
13068
+ const root = path47.resolve(opts.dir ?? process.cwd());
13069
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
12973
13070
  const raw = opts.kind ?? "note";
12974
13071
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
12975
13072
  await appendRuntimeJournalEntry3(paths, { kind, message });
12976
- 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`);
12977
13074
  });
12978
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) => {
12979
- const root = path46.resolve(opts.dir ?? process.cwd());
12980
- const paths = resolveHaivePaths41(findProjectRoot45(root));
13076
+ const root = path47.resolve(opts.dir ?? process.cwd());
13077
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
12981
13078
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
12982
- if (!existsSync64(paths.haiveDir)) {
13079
+ if (!existsSync65(paths.haiveDir)) {
12983
13080
  ui.error("No .ai/ \u2014 run `haive init` first.");
12984
13081
  process.exitCode = 1;
12985
13082
  return;
@@ -12994,13 +13091,13 @@ function registerRuntime(program2) {
12994
13091
  }
12995
13092
 
12996
13093
  // src/commands/memory-timeline.ts
12997
- import { existsSync as existsSync65 } from "fs";
12998
- import path47 from "path";
13094
+ import { existsSync as existsSync66 } from "fs";
13095
+ import path48 from "path";
12999
13096
  import "commander";
13000
13097
  import {
13001
13098
  collectTimelineEntries as collectTimelineEntries2,
13002
- findProjectRoot as findProjectRoot46,
13003
- resolveHaivePaths as resolveHaivePaths42
13099
+ findProjectRoot as findProjectRoot47,
13100
+ resolveHaivePaths as resolveHaivePaths43
13004
13101
  } from "@hiveai/core";
13005
13102
  function registerMemoryTimeline(memory2) {
13006
13103
  memory2.command("timeline").description(
@@ -13011,9 +13108,9 @@ function registerMemoryTimeline(memory2) {
13011
13108
  process.exitCode = 1;
13012
13109
  return;
13013
13110
  }
13014
- const root = path47.resolve(opts.dir ?? process.cwd());
13015
- const paths = resolveHaivePaths42(findProjectRoot46(root));
13016
- if (!existsSync65(paths.memoriesDir)) {
13111
+ const root = path48.resolve(opts.dir ?? process.cwd());
13112
+ const paths = resolveHaivePaths43(findProjectRoot47(root));
13113
+ if (!existsSync66(paths.memoriesDir)) {
13017
13114
  ui.error("No memories \u2014 run `haive init`.");
13018
13115
  process.exitCode = 1;
13019
13116
  return;
@@ -13031,14 +13128,14 @@ function registerMemoryTimeline(memory2) {
13031
13128
  }
13032
13129
 
13033
13130
  // src/commands/memory-conflict-candidates.ts
13034
- import { existsSync as existsSync66 } from "fs";
13035
- import path48 from "path";
13131
+ import { existsSync as existsSync67 } from "fs";
13132
+ import path49 from "path";
13036
13133
  import "commander";
13037
13134
  import {
13038
13135
  findLexicalConflictPairs as findLexicalConflictPairs2,
13039
13136
  findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
13040
- findProjectRoot as findProjectRoot47,
13041
- resolveHaivePaths as resolveHaivePaths43
13137
+ findProjectRoot as findProjectRoot48,
13138
+ resolveHaivePaths as resolveHaivePaths44
13042
13139
  } from "@hiveai/core";
13043
13140
  function parseTypes(csv) {
13044
13141
  const allowed = ["decision", "architecture", "convention", "gotcha"];
@@ -13054,9 +13151,9 @@ function registerMemoryConflictCandidates(memory2) {
13054
13151
  "decision,architecture,convention,gotcha (lexical scan)",
13055
13152
  "decision,architecture"
13056
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) => {
13057
- const root = path48.resolve(opts.dir ?? process.cwd());
13058
- const paths = resolveHaivePaths43(findProjectRoot47(root));
13059
- if (!existsSync66(paths.memoriesDir)) {
13154
+ const root = path49.resolve(opts.dir ?? process.cwd());
13155
+ const paths = resolveHaivePaths44(findProjectRoot48(root));
13156
+ if (!existsSync67(paths.memoriesDir)) {
13060
13157
  ui.error("No memories \u2014 run `haive init`.");
13061
13158
  process.exitCode = 1;
13062
13159
  return;
@@ -13092,20 +13189,20 @@ function registerMemoryConflictCandidates(memory2) {
13092
13189
 
13093
13190
  // src/commands/enforce.ts
13094
13191
  import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
13095
- import { existsSync as existsSync67, statSync as statSync2 } from "fs";
13096
- import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
13097
- 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";
13098
13195
  import "commander";
13099
13196
  import {
13100
- findProjectRoot as findProjectRoot48,
13197
+ findProjectRoot as findProjectRoot49,
13101
13198
  hasRecentBriefingMarker,
13102
13199
  isFreshIsoDate,
13103
- loadConfig as loadConfig10,
13200
+ loadConfig as loadConfig11,
13104
13201
  loadMemoriesFromDir as loadMemoriesFromDir36,
13105
13202
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
13106
13203
  readRecentBriefingMarker,
13107
13204
  resolveBriefingBudget as resolveBriefingBudget3,
13108
- resolveHaivePaths as resolveHaivePaths44,
13205
+ resolveHaivePaths as resolveHaivePaths45,
13109
13206
  saveConfig as saveConfig4,
13110
13207
  SESSION_RECAP_TTL_MS,
13111
13208
  verifyAnchor as verifyAnchor4,
@@ -13118,10 +13215,10 @@ function registerEnforce(program2) {
13118
13215
  "Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
13119
13216
  );
13120
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) => {
13121
- const root = findProjectRoot48(opts.dir);
13122
- const paths = resolveHaivePaths44(root);
13218
+ const root = findProjectRoot49(opts.dir);
13219
+ const paths = resolveHaivePaths45(root);
13123
13220
  await mkdir19(paths.haiveDir, { recursive: true });
13124
- const current = await loadConfig10(paths);
13221
+ const current = await loadConfig11(paths);
13125
13222
  await saveConfig4(paths, {
13126
13223
  ...current,
13127
13224
  enforcement: {
@@ -13144,7 +13241,7 @@ function registerEnforce(program2) {
13144
13241
  if (opts.claude !== false) {
13145
13242
  try {
13146
13243
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
13147
- 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)})`);
13148
13245
  } catch (err) {
13149
13246
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
13150
13247
  }
@@ -13163,21 +13260,21 @@ function registerEnforce(program2) {
13163
13260
  if (report.should_block) process.exit(2);
13164
13261
  });
13165
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) => {
13166
- const root = findProjectRoot48(opts.dir);
13167
- const paths = resolveHaivePaths44(root);
13168
- const cacheDir = path49.join(paths.haiveDir, ".cache");
13169
- if (existsSync67(cacheDir)) {
13170
- 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)`);
13171
13268
  else {
13172
13269
  const removed = await cleanupCacheDir(cacheDir);
13173
- 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)` : ""}`);
13174
13271
  }
13175
13272
  }
13176
- if (existsSync67(paths.runtimeDir)) {
13177
- 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)`);
13178
13275
  else {
13179
13276
  const removed = await cleanupRuntimeDir(paths.runtimeDir);
13180
- 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)` : ""}`);
13181
13278
  }
13182
13279
  }
13183
13280
  });
@@ -13190,8 +13287,8 @@ function registerEnforce(program2) {
13190
13287
  const payload = await readHookPayload();
13191
13288
  const root = resolveRoot(opts.dir, payload);
13192
13289
  if (!root) return;
13193
- const paths = resolveHaivePaths44(root);
13194
- if (!existsSync67(paths.haiveDir)) return;
13290
+ const paths = resolveHaivePaths45(root);
13291
+ if (!existsSync68(paths.haiveDir)) return;
13195
13292
  await mkdir19(paths.runtimeDir, { recursive: true });
13196
13293
  const sessionId = opts.sessionId ?? payload.session_id;
13197
13294
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
@@ -13253,8 +13350,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13253
13350
  const payload = await readHookPayload();
13254
13351
  const root = resolveRoot(opts.dir, payload);
13255
13352
  if (!root) return;
13256
- const paths = resolveHaivePaths44(root);
13257
- if (!existsSync67(paths.haiveDir)) return;
13353
+ const paths = resolveHaivePaths45(root);
13354
+ if (!existsSync68(paths.haiveDir)) return;
13258
13355
  if (!isWriteLikeTool(payload)) return;
13259
13356
  const ok = await hasRecentBriefingMarker(paths, payload.session_id);
13260
13357
  if (ok) {
@@ -13296,9 +13393,9 @@ ${briefing.project_context.content.slice(0, 1800)}`);
13296
13393
  });
13297
13394
  }
13298
13395
  async function runWithEnforcement(command, args, opts) {
13299
- const root = findProjectRoot48(opts.dir);
13300
- const paths = resolveHaivePaths44(root);
13301
- if (!existsSync67(paths.haiveDir)) {
13396
+ const root = findProjectRoot49(opts.dir);
13397
+ const paths = resolveHaivePaths45(root);
13398
+ if (!existsSync68(paths.haiveDir)) {
13302
13399
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
13303
13400
  process.exit(1);
13304
13401
  }
@@ -13317,7 +13414,7 @@ async function runWithEnforcement(command, args, opts) {
13317
13414
  process.exit(2);
13318
13415
  }
13319
13416
  ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
13320
- 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`);
13321
13418
  const child = spawn6(command, args, {
13322
13419
  cwd: root,
13323
13420
  stdio: "inherit",
@@ -13367,9 +13464,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
13367
13464
  source: "haive-run",
13368
13465
  memoryIds: briefing.memories.map((m) => m.id)
13369
13466
  });
13370
- const dir = path49.join(paths.runtimeDir, "enforcement", "briefings");
13467
+ const dir = path50.join(paths.runtimeDir, "enforcement", "briefings");
13371
13468
  await mkdir19(dir, { recursive: true });
13372
- const file = path49.join(dir, `${sessionId}.md`);
13469
+ const file = path50.join(dir, `${sessionId}.md`);
13373
13470
  const parts = [
13374
13471
  "# hAIve Briefing",
13375
13472
  "",
@@ -13391,10 +13488,10 @@ async function writeWrapperBriefing(paths, sessionId, task) {
13391
13488
  return file;
13392
13489
  }
13393
13490
  async function buildEnforcementReport(dir, stage, sessionId) {
13394
- const root = findProjectRoot48(dir);
13395
- const paths = resolveHaivePaths44(root);
13396
- const initialized = existsSync67(paths.haiveDir);
13397
- 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) : {};
13398
13495
  if (initialized) await applyLightweightRepairs(root, paths);
13399
13496
  const mode = config.enforcement?.mode ?? "strict";
13400
13497
  const findings = [];
@@ -13424,7 +13521,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
13424
13521
  findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
13425
13522
  });
13426
13523
  }
13427
- findings.push(...await inspectIntegrationVersions(root, "0.9.28"));
13524
+ findings.push(...await inspectIntegrationVersions(root, "0.9.29"));
13428
13525
  if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
13429
13526
  const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
13430
13527
  findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
@@ -13494,7 +13591,7 @@ function withCategories(report) {
13494
13591
  };
13495
13592
  }
13496
13593
  async function hasRecentSessionRecap(paths) {
13497
- if (!existsSync67(paths.memoriesDir)) return false;
13594
+ if (!existsSync68(paths.memoriesDir)) return false;
13498
13595
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13499
13596
  return all.some(({ memory: memory2 }) => {
13500
13597
  const fm = memory2.frontmatter;
@@ -13503,7 +13600,7 @@ async function hasRecentSessionRecap(paths) {
13503
13600
  });
13504
13601
  }
13505
13602
  async function verifyMemoryPolicy(paths, config) {
13506
- if (!existsSync67(paths.memoriesDir)) return [];
13603
+ if (!existsSync68(paths.memoriesDir)) return [];
13507
13604
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
13508
13605
  const findings = [];
13509
13606
  const staleImportant = [];
@@ -13541,7 +13638,7 @@ async function verifyMemoryPolicy(paths, config) {
13541
13638
  return findings;
13542
13639
  }
13543
13640
  async function verifyDecisionCoverage(paths, stage, sessionId) {
13544
- if (!existsSync67(paths.memoriesDir)) return [];
13641
+ if (!existsSync68(paths.memoriesDir)) return [];
13545
13642
  const changedFiles = await getChangedFiles(paths.root, stage);
13546
13643
  if (changedFiles.length === 0) {
13547
13644
  return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
@@ -13640,16 +13737,16 @@ async function cleanupRuntimeDir(runtimeDir) {
13640
13737
  for (const entry of entries) {
13641
13738
  if (entry.name === ".gitignore" || entry.name === "README.md") continue;
13642
13739
  if (entry.name === "enforcement") {
13643
- removed += await cleanupEnforcementDir(path49.join(runtimeDir, entry.name));
13740
+ removed += await cleanupEnforcementDir(path50.join(runtimeDir, entry.name));
13644
13741
  continue;
13645
13742
  }
13646
- await rm3(path49.join(runtimeDir, entry.name), { recursive: true, force: true });
13743
+ await rm3(path50.join(runtimeDir, entry.name), { recursive: true, force: true });
13647
13744
  removed++;
13648
13745
  }
13649
- await writeFile33(path49.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
13650
- 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"))) {
13651
13748
  await writeFile33(
13652
- path49.join(runtimeDir, "README.md"),
13749
+ path50.join(runtimeDir, "README.md"),
13653
13750
  "# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
13654
13751
  "utf8"
13655
13752
  );
@@ -13662,10 +13759,10 @@ async function cleanupCacheDir(cacheDir) {
13662
13759
  const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
13663
13760
  for (const entry of entries) {
13664
13761
  if (entry.name === ".gitignore") continue;
13665
- await rm3(path49.join(cacheDir, entry.name), { recursive: true, force: true });
13762
+ await rm3(path50.join(cacheDir, entry.name), { recursive: true, force: true });
13666
13763
  removed++;
13667
13764
  }
13668
- await writeFile33(path49.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
13765
+ await writeFile33(path50.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
13669
13766
  return removed;
13670
13767
  }
13671
13768
  async function cleanupEnforcementDir(enforcementDir) {
@@ -13673,7 +13770,7 @@ async function cleanupEnforcementDir(enforcementDir) {
13673
13770
  const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
13674
13771
  for (const entry of entries) {
13675
13772
  if (entry.name === "briefings") continue;
13676
- await rm3(path49.join(enforcementDir, entry.name), { recursive: true, force: true });
13773
+ await rm3(path50.join(enforcementDir, entry.name), { recursive: true, force: true });
13677
13774
  removed++;
13678
13775
  }
13679
13776
  return removed;
@@ -13689,9 +13786,9 @@ async function inspectIntegrationVersions(root, expectedVersion) {
13689
13786
  ];
13690
13787
  const findings = [];
13691
13788
  for (const rel of files) {
13692
- const file = path49.join(root, rel);
13693
- if (!existsSync67(file)) continue;
13694
- 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(() => "");
13695
13792
  for (const bin of extractAbsoluteHaiveBins2(text)) {
13696
13793
  const version = versionForBinary2(bin);
13697
13794
  if (!version) {
@@ -13785,8 +13882,8 @@ function buildScore(findings, threshold = 80) {
13785
13882
  };
13786
13883
  }
13787
13884
  async function installGitEnforcement(root) {
13788
- const hooksDir = path49.join(root, ".git", "hooks");
13789
- if (!existsSync67(path49.join(root, ".git"))) {
13885
+ const hooksDir = path50.join(root, ".git", "hooks");
13886
+ if (!existsSync68(path50.join(root, ".git"))) {
13790
13887
  ui.warn("No .git directory found; git enforcement hooks skipped.");
13791
13888
  return;
13792
13889
  }
@@ -13808,9 +13905,9 @@ haive enforce check --stage pre-push --dir . || exit $?
13808
13905
  }
13809
13906
  ];
13810
13907
  for (const hook of hooks) {
13811
- const file = path49.join(hooksDir, hook.name);
13812
- if (existsSync67(file)) {
13813
- 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(() => "");
13814
13911
  if (current.includes(ENFORCE_HOOK_MARKER)) {
13815
13912
  await writeFile33(file, hook.body, "utf8");
13816
13913
  } else {
@@ -13826,9 +13923,9 @@ ${hook.body}`, "utf8");
13826
13923
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
13827
13924
  }
13828
13925
  async function installCiEnforcement(root) {
13829
- const workflowPath = path49.join(root, ".github", "workflows", "haive-enforcement.yml");
13830
- await mkdir19(path49.dirname(workflowPath), { recursive: true });
13831
- 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)) {
13832
13929
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
13833
13930
  return;
13834
13931
  }
@@ -13856,7 +13953,7 @@ jobs:
13856
13953
  - name: Enforce hAIve policy
13857
13954
  run: haive enforce ci
13858
13955
  `, "utf8");
13859
- ui.success(`Created ${path49.relative(root, workflowPath)}`);
13956
+ ui.success(`Created ${path50.relative(root, workflowPath)}`);
13860
13957
  }
13861
13958
  function printReport(report, json, explain = false) {
13862
13959
  if (json) {
@@ -13916,7 +14013,7 @@ async function readHookPayload() {
13916
14013
  }
13917
14014
  function resolveRoot(dir, payload) {
13918
14015
  try {
13919
- return findProjectRoot48(dir ?? payload.cwd);
14016
+ return findProjectRoot49(dir ?? payload.cwd);
13920
14017
  } catch {
13921
14018
  return null;
13922
14019
  }
@@ -13953,11 +14050,11 @@ function extractToolPaths(payload, root) {
13953
14050
  }
13954
14051
  function normalizeToolPath(file, root) {
13955
14052
  const normalized = file.replace(/\\/g, "/");
13956
- if (!path49.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
13957
- return path49.relative(root, normalized).replace(/\\/g, "/");
14053
+ if (!path50.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
14054
+ return path50.relative(root, normalized).replace(/\\/g, "/");
13958
14055
  }
13959
14056
  async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
13960
- if (!existsSync67(paths.memoriesDir)) return [];
14057
+ if (!existsSync68(paths.memoriesDir)) return [];
13961
14058
  const marker = await readRecentBriefingMarker(paths, sessionId);
13962
14059
  const consulted = new Set(marker?.memory_ids ?? []);
13963
14060
  const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
@@ -14030,8 +14127,8 @@ function registerRun(program2) {
14030
14127
  }
14031
14128
 
14032
14129
  // src/index.ts
14033
- var program = new Command51();
14034
- program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.28").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");
14035
14132
  registerInit(program);
14036
14133
  registerWelcome(program);
14037
14134
  registerResolveProject(program);
@@ -14065,6 +14162,7 @@ registerMemoryApprove(memory);
14065
14162
  registerMemoryUpdate(memory);
14066
14163
  registerMemoryHot(memory);
14067
14164
  registerMemoryTried(memory);
14165
+ registerMemorySeed(memory);
14068
14166
  registerMemoryImport(memory);
14069
14167
  registerMemoryImportChangelog(memory);
14070
14168
  registerMemoryDigest(memory);