@hiveai/cli 0.9.11 → 0.9.13

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 Command50 } from "commander";
4
+ import { Command as Command51 } from "commander";
5
5
 
6
6
  // src/commands/briefing.ts
7
7
  import { existsSync } from "fs";
@@ -198,7 +198,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
198
198
  if (!f) continue;
199
199
  counts.set(f, (counts.get(f) ?? 0) + 1);
200
200
  }
201
- let entries = [...counts.entries()].map(([path47, changes]) => ({ path: path47, changes }));
201
+ let entries = [...counts.entries()].map(([path48, changes]) => ({ path: path48, changes }));
202
202
  const lowerPaths = filePaths.map((p) => p.toLowerCase());
203
203
  if (lowerPaths.length > 0) {
204
204
  entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
@@ -730,23 +730,404 @@ function registerIndexCode(program2) {
730
730
  }
731
731
 
732
732
  // src/commands/init.ts
733
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
734
- import { existsSync as existsSync6 } from "fs";
735
- import path7 from "path";
736
- import { spawnSync } from "child_process";
733
+ import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
734
+ import { existsSync as existsSync7 } from "fs";
735
+ import path8 from "path";
736
+ import { spawnSync as spawnSync2 } from "child_process";
737
737
  import "commander";
738
738
  import {
739
739
  AUTOPILOT_DEFAULTS,
740
740
  buildCodeMap as buildCodeMap2,
741
- resolveHaivePaths as resolveHaivePaths4,
741
+ resolveHaivePaths as resolveHaivePaths5,
742
742
  saveCodeMap as saveCodeMap2,
743
743
  saveConfig
744
744
  } from "@hiveai/core";
745
745
 
746
- // src/commands/init-bootstrap.ts
747
- import { readdir, readFile as readFile2 } from "fs/promises";
746
+ // src/commands/agent.ts
747
+ import { spawnSync } from "child_process";
748
+ import { existsSync as existsSync4 } from "fs";
749
+ import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
750
+ import os2 from "os";
751
+ import path5 from "path";
752
+ import { createInterface } from "readline/promises";
753
+ import "commander";
754
+ import { findProjectRoot as findProjectRoot5, resolveHaivePaths as resolveHaivePaths4 } from "@hiveai/core";
755
+
756
+ // src/commands/init-mcp-setup.ts
757
+ import { readFile as readFile2, writeFile, mkdir as mkdir2 } from "fs/promises";
748
758
  import { existsSync as existsSync3 } from "fs";
749
759
  import path4 from "path";
760
+ import os from "os";
761
+ var HOME = os.homedir();
762
+ var HAIVE_MCP_ENTRY = {
763
+ command: "haive",
764
+ args: ["mcp", "--stdio"]
765
+ };
766
+ function projectMcpEntry(root) {
767
+ return {
768
+ command: "haive",
769
+ args: ["mcp", "--stdio"],
770
+ env: { HAIVE_PROJECT_ROOT: root }
771
+ };
772
+ }
773
+ function cursorMcpPath() {
774
+ return path4.join(HOME, ".cursor", "mcp.json");
775
+ }
776
+ async function configureCursor() {
777
+ const mcpPath = cursorMcpPath();
778
+ const cursorDir = path4.join(HOME, ".cursor");
779
+ if (!existsSync3(cursorDir)) return { client: "Cursor", status: "not_installed" };
780
+ let config = {};
781
+ if (existsSync3(mcpPath)) {
782
+ try {
783
+ config = JSON.parse(await readFile2(mcpPath, "utf8"));
784
+ } catch {
785
+ }
786
+ }
787
+ config.mcpServers ??= {};
788
+ if (config.mcpServers["haive"]) return { client: "Cursor", status: "already_configured" };
789
+ config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
790
+ await mkdir2(cursorDir, { recursive: true });
791
+ await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
792
+ return { client: "Cursor", status: "configured", path: mcpPath };
793
+ }
794
+ function vscodeMcpPath() {
795
+ const candidates = [
796
+ path4.join(HOME, ".config", "Code", "User", "mcp.json"),
797
+ // Linux
798
+ path4.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
799
+ // macOS
800
+ path4.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
801
+ // Windows
802
+ path4.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
803
+ ];
804
+ for (const c of candidates) {
805
+ if (existsSync3(path4.dirname(c))) return c;
806
+ }
807
+ return null;
808
+ }
809
+ async function configureVSCode() {
810
+ const mcpPath = vscodeMcpPath();
811
+ if (!mcpPath) return { client: "VS Code", status: "not_installed" };
812
+ let config = {};
813
+ if (existsSync3(mcpPath)) {
814
+ try {
815
+ config = JSON.parse(await readFile2(mcpPath, "utf8"));
816
+ } catch {
817
+ }
818
+ }
819
+ config.servers ??= {};
820
+ if (config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
821
+ config.servers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
822
+ await mkdir2(path4.dirname(mcpPath), { recursive: true });
823
+ await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
824
+ return { client: "VS Code", status: "configured", path: mcpPath };
825
+ }
826
+ function claudeConfigPath() {
827
+ const p = path4.join(HOME, ".claude.json");
828
+ if (existsSync3(p)) return p;
829
+ const p2 = path4.join(HOME, ".config", "claude", "claude.json");
830
+ if (existsSync3(path4.dirname(p2))) return p2;
831
+ return null;
832
+ }
833
+ async function configureClaude() {
834
+ const cfgPath = claudeConfigPath() ?? path4.join(HOME, ".claude.json");
835
+ if (!existsSync3(cfgPath) && !existsSync3(path4.join(HOME, ".claude"))) {
836
+ return { client: "Claude Code", status: "not_installed" };
837
+ }
838
+ let config = {};
839
+ if (existsSync3(cfgPath)) {
840
+ try {
841
+ config = JSON.parse(await readFile2(cfgPath, "utf8"));
842
+ } catch {
843
+ }
844
+ }
845
+ config.mcpServers ??= {};
846
+ if (config.mcpServers["haive"]) return { client: "Claude Code", status: "already_configured" };
847
+ config.mcpServers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
848
+ await writeFile(cfgPath, JSON.stringify(config, null, 2), "utf8");
849
+ return { client: "Claude Code", status: "configured", path: cfgPath };
850
+ }
851
+ function windsurfMcpPath() {
852
+ const candidates = [
853
+ path4.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
854
+ path4.join(HOME, ".windsurf", "mcp.json")
855
+ ];
856
+ for (const c of candidates) {
857
+ if (existsSync3(path4.dirname(c))) return c;
858
+ }
859
+ return null;
860
+ }
861
+ async function configureWindsurf() {
862
+ const mcpPath = windsurfMcpPath();
863
+ if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
864
+ let config = {};
865
+ if (existsSync3(mcpPath)) {
866
+ try {
867
+ config = JSON.parse(await readFile2(mcpPath, "utf8"));
868
+ } catch {
869
+ }
870
+ }
871
+ config.mcpServers ??= {};
872
+ if (config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
873
+ config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
874
+ await mkdir2(path4.dirname(mcpPath), { recursive: true });
875
+ await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
876
+ return { client: "Windsurf", status: "configured", path: mcpPath };
877
+ }
878
+ async function autoConfigureMcpClients() {
879
+ const results = [];
880
+ const configurators = [configureCursor, configureVSCode, configureClaude, configureWindsurf];
881
+ for (const fn of configurators) {
882
+ try {
883
+ results.push(await fn());
884
+ } catch (err) {
885
+ const name = fn.name.replace("configure", "");
886
+ results.push({ client: name, status: "error", error: String(err) });
887
+ }
888
+ }
889
+ return results;
890
+ }
891
+ async function configureProjectMcpClients(root) {
892
+ const entry = projectMcpEntry(root);
893
+ const results = [];
894
+ try {
895
+ const cursorPath = path4.join(root, ".cursor", "mcp.json");
896
+ let config = {};
897
+ if (existsSync3(cursorPath)) {
898
+ try {
899
+ config = JSON.parse(await readFile2(cursorPath, "utf8"));
900
+ } catch {
901
+ }
902
+ }
903
+ config.mcpServers ??= {};
904
+ config.mcpServers["haive"] = entry;
905
+ await mkdir2(path4.dirname(cursorPath), { recursive: true });
906
+ await writeFile(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
907
+ results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
908
+ } catch (err) {
909
+ results.push({ client: "Cursor (project)", status: "error", error: String(err) });
910
+ }
911
+ try {
912
+ const vscodePath = path4.join(root, ".vscode", "mcp.json");
913
+ let config = {};
914
+ if (existsSync3(vscodePath)) {
915
+ try {
916
+ config = JSON.parse(await readFile2(vscodePath, "utf8"));
917
+ } catch {
918
+ }
919
+ }
920
+ config.servers ??= {};
921
+ config.servers["haive"] = { ...entry, type: "stdio" };
922
+ await mkdir2(path4.dirname(vscodePath), { recursive: true });
923
+ await writeFile(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
924
+ results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
925
+ } catch (err) {
926
+ results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
927
+ }
928
+ try {
929
+ const mcpPath = path4.join(root, ".mcp.json");
930
+ let config = {};
931
+ if (existsSync3(mcpPath)) {
932
+ try {
933
+ config = JSON.parse(await readFile2(mcpPath, "utf8"));
934
+ } catch {
935
+ }
936
+ }
937
+ config.mcpServers ??= {};
938
+ config.mcpServers["haive"] = { ...entry, type: "stdio" };
939
+ await writeFile(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf8");
940
+ results.push({ client: "Claude Code (project)", status: "configured", path: mcpPath });
941
+ } catch (err) {
942
+ results.push({ client: "Claude Code (project)", status: "error", error: String(err) });
943
+ }
944
+ return results;
945
+ }
946
+
947
+ // src/commands/agent.ts
948
+ function registerAgent(program2) {
949
+ const agent = program2.command("agent").description("Detect, configure, and report the best hAIve mode for AI coding agents.");
950
+ agent.command("detect").description("Detect available AI agents and hAIve MCP/wrapper readiness.").option("-d, --dir <dir>", "project root").option("--json", "emit JSON", false).action(async (opts) => {
951
+ const detection = await detectAgentMode(opts.dir);
952
+ printDetection(detection, Boolean(opts.json));
953
+ });
954
+ agent.command("status").description("Alias for agent detect.").option("-d, --dir <dir>", "project root").option("--json", "emit JSON", false).action(async (opts) => {
955
+ const detection = await detectAgentMode(opts.dir);
956
+ printDetection(detection, Boolean(opts.json));
957
+ });
958
+ agent.command("setup").description("Configure hAIve project MCP, optional global MCP clients, and wrapper fallback metadata.").option("-d, --dir <dir>", "project root").option("-y, --yes", "approve user-level/global MCP configuration without prompting", false).option("--no-global", "skip user-level/global MCP configuration").option("--json", "emit JSON", false).action(async (opts) => {
959
+ const result = await setupAgentMode(opts.dir, {
960
+ yes: Boolean(opts.yes),
961
+ global: opts.global !== false && opts.noGlobal !== true,
962
+ interactive: process.stdin.isTTY
963
+ });
964
+ if (opts.json) {
965
+ console.log(JSON.stringify(result, null, 2));
966
+ return;
967
+ }
968
+ printSetupResult(result);
969
+ });
970
+ }
971
+ async function setupAgentMode(dir, opts = {}) {
972
+ const root = findProjectRoot5(dir);
973
+ const paths = resolveHaivePaths4(root);
974
+ const projectResults = await configureProjectMcpClients(root);
975
+ const detectionBeforeGlobal = await detectAgentMode(root);
976
+ let globalResults = [];
977
+ let globalSkippedReason;
978
+ const shouldConsiderGlobal = opts.global !== false;
979
+ if (shouldConsiderGlobal) {
980
+ const approved = opts.yes === true || (opts.interactive ? await confirmGlobalSetup() : false);
981
+ if (approved) {
982
+ globalResults = await autoConfigureMcpClients();
983
+ const codex = await configureCodexIfAvailable(root);
984
+ if (codex) globalResults.push(codex);
985
+ } else {
986
+ globalSkippedReason = opts.interactive ? "User declined user-level/global MCP configuration." : "Non-interactive shell; skipped user-level/global MCP configuration. Re-run `haive agent setup --yes` to apply it.";
987
+ }
988
+ } else {
989
+ globalSkippedReason = "User-level/global MCP configuration disabled.";
990
+ }
991
+ const detection = await detectAgentMode(root);
992
+ const modeFile = await writeAgentModeRecord(paths, detection, globalSkippedReason);
993
+ return {
994
+ detection,
995
+ project_results: projectResults,
996
+ global_results: globalResults,
997
+ mode_file: modeFile,
998
+ ...globalSkippedReason ? { global_skipped_reason: globalSkippedReason } : {}
999
+ };
1000
+ }
1001
+ async function detectAgentMode(dir) {
1002
+ const root = findProjectRoot5(dir);
1003
+ const paths = resolveHaivePaths4(root);
1004
+ const projectMcp = [
1005
+ { client: "Claude Code", path: path5.join(root, ".mcp.json"), present: existsSync4(path5.join(root, ".mcp.json")) },
1006
+ { client: "Cursor", path: path5.join(root, ".cursor", "mcp.json"), present: existsSync4(path5.join(root, ".cursor", "mcp.json")) },
1007
+ { client: "VS Code", path: path5.join(root, ".vscode", "mcp.json"), present: existsSync4(path5.join(root, ".vscode", "mcp.json")) }
1008
+ ];
1009
+ const installedAgents = [
1010
+ { agent: "Codex", command: "codex", installed: commandExists("codex"), mcp_configured: codexMcpConfigured() },
1011
+ { agent: "Claude", command: "claude", installed: commandExists("claude") },
1012
+ { agent: "Aider", command: "aider", installed: commandExists("aider") },
1013
+ { agent: "Cursor", command: "cursor", installed: commandExists("cursor") }
1014
+ ];
1015
+ const hasProjectMcp = projectMcp.some((item) => item.present);
1016
+ const hasNativeMcp = hasProjectMcp || installedAgents.some((a) => a.mcp_configured);
1017
+ const wrapperAgent = installedAgents.find((a) => a.installed && ["codex", "claude", "aider"].includes(a.command));
1018
+ const recommendedMode = hasNativeMcp ? "mcp" : wrapperAgent ? "wrapped" : "fallback";
1019
+ const recommendedCommand = recommendedMode === "mcp" ? "Restart your AI client, then call get_briefing before editing." : recommendedMode === "wrapped" && wrapperAgent ? `haive run -- ${wrapperAgent.command}` : 'haive briefing --task "..." --files "..."';
1020
+ return {
1021
+ root,
1022
+ initialized: existsSync4(paths.haiveDir),
1023
+ project_mcp: projectMcp,
1024
+ installed_agents: installedAgents,
1025
+ recommended_mode: recommendedMode,
1026
+ recommended_command: recommendedCommand
1027
+ };
1028
+ }
1029
+ async function writeAgentModeRecord(paths, detection, skippedReason) {
1030
+ const dir = path5.join(paths.runtimeDir, "enforcement");
1031
+ await mkdir3(dir, { recursive: true });
1032
+ const file = path5.join(dir, "agent-mode.json");
1033
+ const record = {
1034
+ selected_mode: detection.recommended_mode,
1035
+ recommended_command: detection.recommended_command,
1036
+ configured_at: (/* @__PURE__ */ new Date()).toISOString(),
1037
+ project_root: detection.root,
1038
+ notes: [
1039
+ "mcp = native hAIve MCP tools are available or project MCP config exists.",
1040
+ "wrapped = use haive run when native MCP is unavailable.",
1041
+ "fallback = use haive briefing/enforce manually.",
1042
+ ...skippedReason ? [skippedReason] : []
1043
+ ]
1044
+ };
1045
+ await writeFile2(file, JSON.stringify(record, null, 2) + "\n", "utf8");
1046
+ return file;
1047
+ }
1048
+ async function confirmGlobalSetup() {
1049
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1050
+ try {
1051
+ const answer = await rl.question(
1052
+ "Configure hAIve in user-level AI client configs (Cursor/VS Code/Claude/Codex when detected)? [y/N] "
1053
+ );
1054
+ return /^y(es)?$/i.test(answer.trim());
1055
+ } finally {
1056
+ rl.close();
1057
+ }
1058
+ }
1059
+ async function configureCodexIfAvailable(root) {
1060
+ if (!commandExists("codex")) return { client: "Codex", status: "not_installed" };
1061
+ if (codexMcpConfigured()) return { client: "Codex", status: "already_configured" };
1062
+ const result = spawnSync("codex", [
1063
+ "mcp",
1064
+ "add",
1065
+ "haive",
1066
+ "--env",
1067
+ `HAIVE_PROJECT_ROOT=${root}`,
1068
+ "--",
1069
+ "haive",
1070
+ "mcp",
1071
+ "--stdio"
1072
+ ], { encoding: "utf8" });
1073
+ if (result.status === 0) return { client: "Codex", status: "configured", path: path5.join(os2.homedir(), ".codex", "config.toml") };
1074
+ return { client: "Codex", status: "error", error: result.stderr || result.stdout || "codex mcp add failed" };
1075
+ }
1076
+ function commandExists(command) {
1077
+ const result = spawnSync(process.platform === "win32" ? "where" : "which", [command], {
1078
+ encoding: "utf8",
1079
+ stdio: ["ignore", "pipe", "ignore"]
1080
+ });
1081
+ return result.status === 0;
1082
+ }
1083
+ function codexMcpConfigured() {
1084
+ if (!commandExists("codex")) return false;
1085
+ const result = spawnSync("codex", ["mcp", "get", "haive"], {
1086
+ encoding: "utf8",
1087
+ stdio: ["ignore", "pipe", "ignore"]
1088
+ });
1089
+ return result.status === 0;
1090
+ }
1091
+ function printDetection(detection, json) {
1092
+ if (json) {
1093
+ console.log(JSON.stringify(detection, null, 2));
1094
+ return;
1095
+ }
1096
+ console.log(ui.bold("hAIve agent status"));
1097
+ console.log(ui.dim(` root: ${detection.root}`));
1098
+ console.log(`${detection.initialized ? ui.green("\u2713") : ui.red("\u2717")} project initialized`);
1099
+ for (const cfg of detection.project_mcp) {
1100
+ console.log(`${cfg.present ? ui.green("\u2713") : ui.yellow("\u2022")} ${cfg.client} project MCP ${ui.dim(path5.relative(detection.root, cfg.path))}`);
1101
+ }
1102
+ for (const agent of detection.installed_agents) {
1103
+ const marker = agent.installed ? ui.green("\u2713") : ui.dim("\u2022");
1104
+ const mcp = agent.mcp_configured === true ? " + hAIve MCP" : "";
1105
+ console.log(`${marker} ${agent.agent} (${agent.command})${mcp}`);
1106
+ }
1107
+ console.log(ui.bold(`Recommended mode: ${detection.recommended_mode}`));
1108
+ console.log(` ${detection.recommended_command}`);
1109
+ }
1110
+ function printSetupResult(result) {
1111
+ for (const item of result.project_results) {
1112
+ if (item.status === "configured") ui.success(`${item.client} project MCP config written (${item.path})`);
1113
+ else if (item.status === "already_configured") ui.info(`${item.client} already configured`);
1114
+ else if (item.status === "error") ui.warn(`${item.client}: ${item.error}`);
1115
+ }
1116
+ for (const item of result.global_results) {
1117
+ if (item.status === "configured") ui.success(`${item.client} user-level MCP configured${item.path ? ` (${item.path})` : ""}`);
1118
+ else if (item.status === "already_configured") ui.info(`${item.client} user-level MCP already configured`);
1119
+ else if (item.status === "not_installed") ui.info(`${item.client} not detected`);
1120
+ else if (item.status === "error") ui.warn(`${item.client}: ${item.error}`);
1121
+ }
1122
+ if (result.global_skipped_reason) ui.warn(result.global_skipped_reason);
1123
+ ui.success(`Agent mode recorded at ${result.mode_file}`);
1124
+ printDetection(result.detection, false);
1125
+ }
1126
+
1127
+ // src/commands/init-bootstrap.ts
1128
+ import { readdir, readFile as readFile3 } from "fs/promises";
1129
+ import { existsSync as existsSync5 } from "fs";
1130
+ import path6 from "path";
750
1131
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
751
1132
  "node_modules",
752
1133
  "dist",
@@ -832,12 +1213,12 @@ function detectKeyDeps(allDeps) {
832
1213
  return KEY_DEPS.filter((d) => allDeps[d] !== void 0);
833
1214
  }
834
1215
  function detectLanguage(root) {
835
- if (existsSync3(path4.join(root, "tsconfig.json"))) return "TypeScript";
836
- if (existsSync3(path4.join(root, "pyproject.toml")) || existsSync3(path4.join(root, "setup.py"))) return "Python";
837
- if (existsSync3(path4.join(root, "go.mod"))) return "Go";
838
- if (existsSync3(path4.join(root, "pom.xml")) || existsSync3(path4.join(root, "build.gradle"))) return "Java/Kotlin";
839
- if (existsSync3(path4.join(root, "Cargo.toml"))) return "Rust";
840
- if (existsSync3(path4.join(root, "package.json"))) return "JavaScript";
1216
+ if (existsSync5(path6.join(root, "tsconfig.json"))) return "TypeScript";
1217
+ if (existsSync5(path6.join(root, "pyproject.toml")) || existsSync5(path6.join(root, "setup.py"))) return "Python";
1218
+ if (existsSync5(path6.join(root, "go.mod"))) return "Go";
1219
+ if (existsSync5(path6.join(root, "pom.xml")) || existsSync5(path6.join(root, "build.gradle"))) return "Java/Kotlin";
1220
+ if (existsSync5(path6.join(root, "Cargo.toml"))) return "Rust";
1221
+ if (existsSync5(path6.join(root, "package.json"))) return "JavaScript";
841
1222
  return "Unknown";
842
1223
  }
843
1224
  function detectProjectType(frameworks, scripts, isMonorepo) {
@@ -854,7 +1235,7 @@ function detectProjectType(frameworks, scripts, isMonorepo) {
854
1235
  if (frameworks.includes("Express") || frameworks.includes("Fastify") || frameworks.includes("Hono")) return "Backend API";
855
1236
  if (frameworks.includes("React") || frameworks.includes("Vue") || frameworks.includes("Svelte")) return "Frontend SPA";
856
1237
  if (scripts["build"] && !scripts["dev"]) return "CLI tool / library";
857
- if (existsSync3("pom.xml")) return "Java backend";
1238
+ if (existsSync5("pom.xml")) return "Java backend";
858
1239
  return "Application";
859
1240
  }
860
1241
  async function scanDirs(root, maxDepth = 2) {
@@ -870,9 +1251,9 @@ async function scanDirs(root, maxDepth = 2) {
870
1251
  for (const entry of entries) {
871
1252
  if (!entry.isDirectory()) continue;
872
1253
  if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
873
- const rel = path4.relative(root, path4.join(dir, entry.name));
1254
+ const rel = path6.relative(root, path6.join(dir, entry.name));
874
1255
  results.push(rel);
875
- await walk(path4.join(dir, entry.name), depth + 1);
1256
+ await walk(path6.join(dir, entry.name), depth + 1);
876
1257
  }
877
1258
  }
878
1259
  await walk(root, 0);
@@ -989,10 +1370,10 @@ function readmeExcerpt(readme) {
989
1370
  }
990
1371
  async function generateBootstrapContext(root) {
991
1372
  let pkg = {};
992
- const pkgPath = path4.join(root, "package.json");
993
- if (existsSync3(pkgPath)) {
1373
+ const pkgPath = path6.join(root, "package.json");
1374
+ if (existsSync5(pkgPath)) {
994
1375
  try {
995
- pkg = JSON.parse(await readFile2(pkgPath, "utf8"));
1376
+ pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
996
1377
  } catch {
997
1378
  }
998
1379
  }
@@ -1002,14 +1383,14 @@ async function generateBootstrapContext(root) {
1002
1383
  const language = detectLanguage(root);
1003
1384
  const isMonorepo = pkg.workspaces !== void 0 && (Array.isArray(pkg.workspaces) ? pkg.workspaces.length > 0 : true);
1004
1385
  const projectType = detectProjectType(frameworks, pkg.scripts ?? {}, isMonorepo);
1005
- const projectName = pkg.name ?? path4.basename(root);
1386
+ const projectName = pkg.name ?? path6.basename(root);
1006
1387
  const projectDesc = pkg.description ?? "";
1007
1388
  let readmeSummary = "";
1008
1389
  for (const name of ["README.md", "readme.md", "README"]) {
1009
- const p = path4.join(root, name);
1010
- if (existsSync3(p)) {
1390
+ const p = path6.join(root, name);
1391
+ if (existsSync5(p)) {
1011
1392
  try {
1012
- const content = await readFile2(p, "utf8");
1393
+ const content = await readFile3(p, "utf8");
1013
1394
  readmeSummary = readmeExcerpt(content);
1014
1395
  break;
1015
1396
  } catch {
@@ -1071,201 +1452,10 @@ async function generateBootstrapContext(root) {
1071
1452
  return lines.join("\n");
1072
1453
  }
1073
1454
 
1074
- // src/commands/init-mcp-setup.ts
1075
- import { readFile as readFile3, writeFile, mkdir as mkdir2 } from "fs/promises";
1076
- import { existsSync as existsSync4 } from "fs";
1077
- import path5 from "path";
1078
- import os from "os";
1079
- var HOME = os.homedir();
1080
- var HAIVE_MCP_ENTRY = {
1081
- command: "haive",
1082
- args: ["mcp", "--stdio"]
1083
- };
1084
- function projectMcpEntry(root) {
1085
- return {
1086
- command: "haive",
1087
- args: ["mcp", "--stdio"],
1088
- env: { HAIVE_PROJECT_ROOT: root }
1089
- };
1090
- }
1091
- function cursorMcpPath() {
1092
- return path5.join(HOME, ".cursor", "mcp.json");
1093
- }
1094
- async function configureCursor() {
1095
- const mcpPath = cursorMcpPath();
1096
- const cursorDir = path5.join(HOME, ".cursor");
1097
- if (!existsSync4(cursorDir)) return { client: "Cursor", status: "not_installed" };
1098
- let config = {};
1099
- if (existsSync4(mcpPath)) {
1100
- try {
1101
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1102
- } catch {
1103
- }
1104
- }
1105
- config.mcpServers ??= {};
1106
- if (config.mcpServers["haive"]) return { client: "Cursor", status: "already_configured" };
1107
- config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
1108
- await mkdir2(cursorDir, { recursive: true });
1109
- await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
1110
- return { client: "Cursor", status: "configured", path: mcpPath };
1111
- }
1112
- function vscodeMcpPath() {
1113
- const candidates = [
1114
- path5.join(HOME, ".config", "Code", "User", "mcp.json"),
1115
- // Linux
1116
- path5.join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
1117
- // macOS
1118
- path5.join(HOME, "AppData", "Roaming", "Code", "User", "mcp.json"),
1119
- // Windows
1120
- path5.join(HOME, ".config", "Code - Insiders", "User", "mcp.json")
1121
- ];
1122
- for (const c of candidates) {
1123
- if (existsSync4(path5.dirname(c))) return c;
1124
- }
1125
- return null;
1126
- }
1127
- async function configureVSCode() {
1128
- const mcpPath = vscodeMcpPath();
1129
- if (!mcpPath) return { client: "VS Code", status: "not_installed" };
1130
- let config = {};
1131
- if (existsSync4(mcpPath)) {
1132
- try {
1133
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1134
- } catch {
1135
- }
1136
- }
1137
- config.servers ??= {};
1138
- if (config.servers["haive"]) return { client: "VS Code", status: "already_configured" };
1139
- config.servers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1140
- await mkdir2(path5.dirname(mcpPath), { recursive: true });
1141
- await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
1142
- return { client: "VS Code", status: "configured", path: mcpPath };
1143
- }
1144
- function claudeConfigPath() {
1145
- const p = path5.join(HOME, ".claude.json");
1146
- if (existsSync4(p)) return p;
1147
- const p2 = path5.join(HOME, ".config", "claude", "claude.json");
1148
- if (existsSync4(path5.dirname(p2))) return p2;
1149
- return null;
1150
- }
1151
- async function configureClaude() {
1152
- const cfgPath = claudeConfigPath() ?? path5.join(HOME, ".claude.json");
1153
- if (!existsSync4(cfgPath) && !existsSync4(path5.join(HOME, ".claude"))) {
1154
- return { client: "Claude Code", status: "not_installed" };
1155
- }
1156
- let config = {};
1157
- if (existsSync4(cfgPath)) {
1158
- try {
1159
- config = JSON.parse(await readFile3(cfgPath, "utf8"));
1160
- } catch {
1161
- }
1162
- }
1163
- config.mcpServers ??= {};
1164
- if (config.mcpServers["haive"]) return { client: "Claude Code", status: "already_configured" };
1165
- config.mcpServers["haive"] = { ...HAIVE_MCP_ENTRY, type: "stdio" };
1166
- await writeFile(cfgPath, JSON.stringify(config, null, 2), "utf8");
1167
- return { client: "Claude Code", status: "configured", path: cfgPath };
1168
- }
1169
- function windsurfMcpPath() {
1170
- const candidates = [
1171
- path5.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
1172
- path5.join(HOME, ".windsurf", "mcp.json")
1173
- ];
1174
- for (const c of candidates) {
1175
- if (existsSync4(path5.dirname(c))) return c;
1176
- }
1177
- return null;
1178
- }
1179
- async function configureWindsurf() {
1180
- const mcpPath = windsurfMcpPath();
1181
- if (!mcpPath) return { client: "Windsurf", status: "not_installed" };
1182
- let config = {};
1183
- if (existsSync4(mcpPath)) {
1184
- try {
1185
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1186
- } catch {
1187
- }
1188
- }
1189
- config.mcpServers ??= {};
1190
- if (config.mcpServers["haive"]) return { client: "Windsurf", status: "already_configured" };
1191
- config.mcpServers["haive"] = HAIVE_MCP_ENTRY;
1192
- await mkdir2(path5.dirname(mcpPath), { recursive: true });
1193
- await writeFile(mcpPath, JSON.stringify(config, null, 2), "utf8");
1194
- return { client: "Windsurf", status: "configured", path: mcpPath };
1195
- }
1196
- async function autoConfigureMcpClients() {
1197
- const results = [];
1198
- const configurators = [configureCursor, configureVSCode, configureClaude, configureWindsurf];
1199
- for (const fn of configurators) {
1200
- try {
1201
- results.push(await fn());
1202
- } catch (err) {
1203
- const name = fn.name.replace("configure", "");
1204
- results.push({ client: name, status: "error", error: String(err) });
1205
- }
1206
- }
1207
- return results;
1208
- }
1209
- async function configureProjectMcpClients(root) {
1210
- const entry = projectMcpEntry(root);
1211
- const results = [];
1212
- try {
1213
- const cursorPath = path5.join(root, ".cursor", "mcp.json");
1214
- let config = {};
1215
- if (existsSync4(cursorPath)) {
1216
- try {
1217
- config = JSON.parse(await readFile3(cursorPath, "utf8"));
1218
- } catch {
1219
- }
1220
- }
1221
- config.mcpServers ??= {};
1222
- config.mcpServers["haive"] = entry;
1223
- await mkdir2(path5.dirname(cursorPath), { recursive: true });
1224
- await writeFile(cursorPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1225
- results.push({ client: "Cursor (project)", status: "configured", path: cursorPath });
1226
- } catch (err) {
1227
- results.push({ client: "Cursor (project)", status: "error", error: String(err) });
1228
- }
1229
- try {
1230
- const vscodePath = path5.join(root, ".vscode", "mcp.json");
1231
- let config = {};
1232
- if (existsSync4(vscodePath)) {
1233
- try {
1234
- config = JSON.parse(await readFile3(vscodePath, "utf8"));
1235
- } catch {
1236
- }
1237
- }
1238
- config.servers ??= {};
1239
- config.servers["haive"] = { ...entry, type: "stdio" };
1240
- await mkdir2(path5.dirname(vscodePath), { recursive: true });
1241
- await writeFile(vscodePath, JSON.stringify(config, null, 2) + "\n", "utf8");
1242
- results.push({ client: "VS Code (workspace)", status: "configured", path: vscodePath });
1243
- } catch (err) {
1244
- results.push({ client: "VS Code (workspace)", status: "error", error: String(err) });
1245
- }
1246
- try {
1247
- const mcpPath = path5.join(root, ".mcp.json");
1248
- let config = {};
1249
- if (existsSync4(mcpPath)) {
1250
- try {
1251
- config = JSON.parse(await readFile3(mcpPath, "utf8"));
1252
- } catch {
1253
- }
1254
- }
1255
- config.mcpServers ??= {};
1256
- config.mcpServers["haive"] = { ...entry, type: "stdio" };
1257
- await writeFile(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1258
- results.push({ client: "Claude Code (project)", status: "configured", path: mcpPath });
1259
- } catch (err) {
1260
- results.push({ client: "Claude Code (project)", status: "error", error: String(err) });
1261
- }
1262
- return results;
1263
- }
1264
-
1265
1455
  // src/commands/init-stack-packs.ts
1266
- import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
1267
- import { existsSync as existsSync5 } from "fs";
1268
- import path6 from "path";
1456
+ import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
1457
+ import { existsSync as existsSync6 } from "fs";
1458
+ import path7 from "path";
1269
1459
  import {
1270
1460
  buildFrontmatter,
1271
1461
  memoryFilePath,
@@ -1877,7 +2067,7 @@ function autoDetectStacks(deps) {
1877
2067
  async function seedStackPack(haivePaths, stack) {
1878
2068
  const memories = PACKS[stack];
1879
2069
  if (!memories) return 0;
1880
- await mkdir3(haivePaths.teamDir, { recursive: true });
2070
+ await mkdir4(haivePaths.teamDir, { recursive: true });
1881
2071
  let count = 0;
1882
2072
  for (const mem of memories) {
1883
2073
  const fm = buildFrontmatter({
@@ -1888,10 +2078,10 @@ async function seedStackPack(haivePaths, stack) {
1888
2078
  tags: mem.tags
1889
2079
  });
1890
2080
  const filePath = memoryFilePath(haivePaths, "team", fm.id);
1891
- if (existsSync5(filePath)) continue;
2081
+ if (existsSync6(filePath)) continue;
1892
2082
  const content = serializeMemory({ frontmatter: fm, body: mem.body });
1893
- await mkdir3(path6.dirname(filePath), { recursive: true });
1894
- await writeFile2(filePath, content, "utf8");
2083
+ await mkdir4(path7.dirname(filePath), { recursive: true });
2084
+ await writeFile3(filePath, content, "utf8");
1895
2085
  count++;
1896
2086
  }
1897
2087
  return count;
@@ -2105,38 +2295,42 @@ function registerInit(program2) {
2105
2295
  ).option(
2106
2296
  "--no-mcp-setup",
2107
2297
  "skip auto-configuring haive MCP (haive mcp --stdio) in Cursor / VS Code / Claude Code"
2298
+ ).option(
2299
+ "-y, --yes",
2300
+ "approve user-level AI client configuration prompts during agent setup",
2301
+ false
2108
2302
  ).action(async (opts) => {
2109
- const root = path7.resolve(opts.dir);
2110
- const paths = resolveHaivePaths4(root);
2303
+ const root = path8.resolve(opts.dir);
2304
+ const paths = resolveHaivePaths5(root);
2111
2305
  const autopilot = opts.manual !== true;
2112
2306
  const wantBootstrap = opts.bootstrap === void 0 ? autopilot : opts.bootstrap;
2113
2307
  const wantStack = opts.stack === void 0 ? autopilot ? "auto" : void 0 : opts.stack === "none" ? void 0 : opts.stack;
2114
- if (existsSync6(paths.haiveDir)) {
2308
+ if (existsSync7(paths.haiveDir)) {
2115
2309
  ui.warn(`.ai/ already exists at ${paths.haiveDir} \u2014 leaving existing files in place.`);
2116
2310
  }
2117
- await mkdir4(paths.personalDir, { recursive: true });
2118
- await mkdir4(paths.teamDir, { recursive: true });
2119
- await mkdir4(paths.moduleDir, { recursive: true });
2120
- await mkdir4(paths.modulesContextDir, { recursive: true });
2311
+ await mkdir5(paths.personalDir, { recursive: true });
2312
+ await mkdir5(paths.teamDir, { recursive: true });
2313
+ await mkdir5(paths.moduleDir, { recursive: true });
2314
+ await mkdir5(paths.modulesContextDir, { recursive: true });
2121
2315
  await ensureAiRuntimeLayout(paths.runtimeDir);
2122
- if (!existsSync6(paths.projectContext)) {
2316
+ if (!existsSync7(paths.projectContext)) {
2123
2317
  if (wantBootstrap) {
2124
2318
  ui.info("Bootstrapping project context from local files\u2026");
2125
2319
  try {
2126
2320
  const context = await generateBootstrapContext(root);
2127
- await writeFile3(paths.projectContext, context, "utf8");
2321
+ await writeFile4(paths.projectContext, context, "utf8");
2128
2322
  ui.success("Created .ai/project-context.md (auto-bootstrapped from local files)");
2129
2323
  } catch (err) {
2130
2324
  ui.warn(`Bootstrap failed (${String(err)}) \u2014 writing default template instead`);
2131
- await writeFile3(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
2325
+ await writeFile4(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
2132
2326
  }
2133
2327
  } else {
2134
- await writeFile3(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
2135
- ui.success(`Created ${path7.relative(root, paths.projectContext)}`);
2328
+ await writeFile4(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
2329
+ ui.success(`Created ${path8.relative(root, paths.projectContext)}`);
2136
2330
  }
2137
2331
  }
2138
- const configExists = existsSync6(
2139
- path7.join(paths.haiveDir, "haive.config.json")
2332
+ const configExists = existsSync7(
2333
+ path8.join(paths.haiveDir, "haive.config.json")
2140
2334
  );
2141
2335
  if (!configExists) {
2142
2336
  await saveConfig(paths, autopilot ? AUTOPILOT_DEFAULTS : { autopilot: false });
@@ -2147,7 +2341,7 @@ function registerInit(program2) {
2147
2341
  if (opts.bridges) {
2148
2342
  await writeBridge(root, "CLAUDE.md");
2149
2343
  await writeBridge(root, ".cursorrules");
2150
- await writeBridge(root, path7.join(".github", "copilot-instructions.md"));
2344
+ await writeBridge(root, path8.join(".github", "copilot-instructions.md"));
2151
2345
  await writeCursorHaiveRule(root);
2152
2346
  }
2153
2347
  const stacksToSeed = await resolveStacksToSeed(root, wantStack);
@@ -2170,18 +2364,18 @@ function registerInit(program2) {
2170
2364
  }
2171
2365
  const wantCi = opts.withCi || autopilot;
2172
2366
  if (wantCi) {
2173
- const ciPath = path7.join(root, ".github", "workflows", "haive-sync.yml");
2174
- if (existsSync6(ciPath)) {
2367
+ const ciPath = path8.join(root, ".github", "workflows", "haive-sync.yml");
2368
+ if (existsSync7(ciPath)) {
2175
2369
  ui.info("CI workflow already exists \u2014 skipped");
2176
2370
  } else {
2177
- await mkdir4(path7.dirname(ciPath), { recursive: true });
2178
- await writeFile3(ciPath, CI_WORKFLOW, "utf8");
2179
- ui.success(`Created ${path7.relative(root, ciPath)}`);
2371
+ await mkdir5(path8.dirname(ciPath), { recursive: true });
2372
+ await writeFile4(ciPath, CI_WORKFLOW, "utf8");
2373
+ ui.success(`Created ${path8.relative(root, ciPath)}`);
2180
2374
  }
2181
2375
  }
2182
2376
  if (autopilot) {
2183
2377
  const haiveBin = process.argv[1];
2184
- const enforcementResult = spawnSync(
2378
+ const enforcementResult = spawnSync2(
2185
2379
  process.execPath,
2186
2380
  [haiveBin, "enforce", "install", "--dir", root],
2187
2381
  { encoding: "utf8" }
@@ -2201,36 +2395,28 @@ function registerInit(program2) {
2201
2395
  }
2202
2396
  }
2203
2397
  if (opts.mcpSetup !== false) {
2204
- const mcpResults = await autoConfigureMcpClients();
2205
- const configured = mcpResults.filter((r) => r.status === "configured");
2206
- const alreadyOk = mcpResults.filter((r) => r.status === "already_configured");
2207
- for (const r of configured) {
2208
- ui.success(`haive MCP configured in ${r.client} (${r.path})`);
2209
- }
2210
- for (const r of alreadyOk) {
2211
- ui.info(`haive MCP already configured in ${r.client} \u2014 skipped`);
2212
- }
2213
- if (configured.length === 0 && alreadyOk.length === 0) {
2214
- ui.warn(
2215
- 'No supported AI client detected (Cursor, VS Code, Claude Code, Windsurf).\n Configure manually: command "haive", args ["mcp", "--stdio"].\n See: https://github.com/Doucs91/hAIve#mcp-setup'
2216
- );
2398
+ const agentSetup = await setupAgentMode(root, {
2399
+ yes: opts.yes === true,
2400
+ global: true,
2401
+ interactive: process.stdin.isTTY
2402
+ });
2403
+ for (const r of agentSetup.project_results) {
2404
+ if (r.status === "configured" && r.path) ui.success(`haive MCP project config written (${path8.relative(root, r.path)})`);
2405
+ else if (r.status === "error") ui.warn(`${r.client}: ${r.error}`);
2217
2406
  }
2218
- const projectMcpResults = await configureProjectMcpClients(root);
2219
- for (const r of projectMcpResults) {
2220
- if (r.status === "configured") {
2221
- ui.success(`haive MCP project config written (${path7.relative(root, r.path)})`);
2222
- }
2407
+ for (const r of agentSetup.global_results) {
2408
+ if (r.status === "configured") ui.success(`haive MCP configured in ${r.client}${r.path ? ` (${r.path})` : ""}`);
2409
+ else if (r.status === "already_configured") ui.info(`haive MCP already configured in ${r.client} \u2014 skipped`);
2223
2410
  }
2411
+ if (agentSetup.global_skipped_reason) ui.warn(agentSetup.global_skipped_reason);
2412
+ ui.info(`Recommended agent mode: ${agentSetup.detection.recommended_mode}`);
2413
+ ui.info(agentSetup.detection.recommended_command);
2224
2414
  await ensureGitignoreEntries(root, [
2225
2415
  ".cursor/mcp.json",
2226
2416
  ".vscode/mcp.json",
2227
2417
  ".mcp.json"
2228
2418
  ]);
2229
- if (configured.length > 0 || projectMcpResults.some((r) => r.status === "configured")) {
2230
- ui.info(
2231
- ui.dim(" \u2192 Restart your AI client for MCP changes to take effect.")
2232
- );
2233
- }
2419
+ ui.info(ui.dim(" \u2192 Restart your AI client for MCP changes to take effect."));
2234
2420
  }
2235
2421
  ui.success(`hAIve initialized at ${root}${autopilot ? " (autopilot mode)" : ""}`);
2236
2422
  console.log();
@@ -2279,8 +2465,8 @@ function registerInit(program2) {
2279
2465
  async function resolveStacksToSeed(root, stackOpt) {
2280
2466
  if (!stackOpt) return [];
2281
2467
  if (stackOpt === "auto") {
2282
- const pkgPath = path7.join(root, "package.json");
2283
- if (!existsSync6(pkgPath)) return [];
2468
+ const pkgPath = path8.join(root, "package.json");
2469
+ if (!existsSync7(pkgPath)) return [];
2284
2470
  try {
2285
2471
  const pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
2286
2472
  const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -2293,23 +2479,23 @@ async function resolveStacksToSeed(root, stackOpt) {
2293
2479
  }
2294
2480
  async function writeCursorHaiveRule(root) {
2295
2481
  const relPath = ".cursor/rules/haive-mcp-required.mdc";
2296
- const target = path7.join(root, relPath);
2297
- if (existsSync6(target)) {
2482
+ const target = path8.join(root, relPath);
2483
+ if (existsSync7(target)) {
2298
2484
  ui.info(`Cursor rule ${relPath} already exists \u2014 skipped`);
2299
2485
  return;
2300
2486
  }
2301
- await mkdir4(path7.dirname(target), { recursive: true });
2302
- await writeFile3(target, CURSOR_HAIVE_RULE_MDC, "utf8");
2487
+ await mkdir5(path8.dirname(target), { recursive: true });
2488
+ await writeFile4(target, CURSOR_HAIVE_RULE_MDC, "utf8");
2303
2489
  ui.success(`Created Cursor rule ${relPath}`);
2304
2490
  }
2305
2491
  async function writeBridge(root, relPath) {
2306
- const target = path7.join(root, relPath);
2307
- if (existsSync6(target)) {
2492
+ const target = path8.join(root, relPath);
2493
+ if (existsSync7(target)) {
2308
2494
  ui.info(`Bridge ${relPath} already exists \u2014 skipped`);
2309
2495
  return;
2310
2496
  }
2311
- await mkdir4(path7.dirname(target), { recursive: true });
2312
- await writeFile3(target, BRIDGE_BODY, "utf8");
2497
+ await mkdir5(path8.dirname(target), { recursive: true });
2498
+ await writeFile4(target, BRIDGE_BODY, "utf8");
2313
2499
  ui.success(`Created bridge ${relPath}`);
2314
2500
  }
2315
2501
  var RUNTIME_README_BODY = `# .ai/.runtime \u2014 disposable local layer
@@ -2325,43 +2511,43 @@ var RUNTIME_GITIGNORE_BODY = `*
2325
2511
  !README.md
2326
2512
  `;
2327
2513
  async function ensureAiRuntimeLayout(runtimeDir) {
2328
- await mkdir4(runtimeDir, { recursive: true });
2329
- const gi = path7.join(runtimeDir, ".gitignore");
2330
- if (!existsSync6(gi)) {
2331
- await writeFile3(gi, RUNTIME_GITIGNORE_BODY, "utf8");
2514
+ await mkdir5(runtimeDir, { recursive: true });
2515
+ const gi = path8.join(runtimeDir, ".gitignore");
2516
+ if (!existsSync7(gi)) {
2517
+ await writeFile4(gi, RUNTIME_GITIGNORE_BODY, "utf8");
2332
2518
  }
2333
- const readme = path7.join(runtimeDir, "README.md");
2334
- if (!existsSync6(readme)) {
2335
- await writeFile3(readme, RUNTIME_README_BODY, "utf8");
2519
+ const readme = path8.join(runtimeDir, "README.md");
2520
+ if (!existsSync7(readme)) {
2521
+ await writeFile4(readme, RUNTIME_README_BODY, "utf8");
2336
2522
  }
2337
2523
  }
2338
2524
  async function ensureGitignoreEntries(root, patterns) {
2339
2525
  try {
2340
- const gitignorePath = path7.join(root, ".gitignore");
2526
+ const gitignorePath = path8.join(root, ".gitignore");
2341
2527
  let existing = "";
2342
- if (existsSync6(gitignorePath)) {
2528
+ if (existsSync7(gitignorePath)) {
2343
2529
  existing = await readFile4(gitignorePath, "utf8");
2344
2530
  }
2345
2531
  const lines = existing.split("\n");
2346
2532
  const missing = patterns.filter((p) => !lines.some((l) => l.trim() === p));
2347
2533
  if (missing.length === 0) return;
2348
2534
  const toAppend = (existing.endsWith("\n") || existing === "" ? "" : "\n") + "# hAIve project-level MCP configs (machine-specific absolute paths)\n" + missing.join("\n") + "\n";
2349
- await writeFile3(gitignorePath, existing + toAppend, "utf8");
2535
+ await writeFile4(gitignorePath, existing + toAppend, "utf8");
2350
2536
  } catch {
2351
2537
  }
2352
2538
  }
2353
2539
 
2354
2540
  // src/commands/install-hooks.ts
2355
- import { mkdir as mkdir6, writeFile as writeFile5, chmod, readFile as readFile6 } from "fs/promises";
2356
- import { existsSync as existsSync8 } from "fs";
2357
- import path9 from "path";
2541
+ import { mkdir as mkdir7, writeFile as writeFile6, chmod, readFile as readFile6 } from "fs/promises";
2542
+ import { existsSync as existsSync9 } from "fs";
2543
+ import path10 from "path";
2358
2544
  import "commander";
2359
- import { findProjectRoot as findProjectRoot6 } from "@hiveai/core";
2545
+ import { findProjectRoot as findProjectRoot7 } from "@hiveai/core";
2360
2546
 
2361
2547
  // src/utils/claude-hooks.ts
2362
- import { existsSync as existsSync7 } from "fs";
2363
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
2364
- import path8 from "path";
2548
+ import { existsSync as existsSync8 } from "fs";
2549
+ import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
2550
+ import path9 from "path";
2365
2551
  var HAIVE_HOOK_TAG = "haive-enforcement";
2366
2552
  var POST_TOOL_USE_GROUP = {
2367
2553
  matcher: "Edit|Write|Bash",
@@ -2447,7 +2633,7 @@ function unpatchClaudeSettings(input) {
2447
2633
  async function installClaudeHooksAtPath(settingsPath) {
2448
2634
  let raw = null;
2449
2635
  let created = false;
2450
- if (existsSync7(settingsPath)) {
2636
+ if (existsSync8(settingsPath)) {
2451
2637
  try {
2452
2638
  raw = JSON.parse(await readFile5(settingsPath, "utf8"));
2453
2639
  } catch {
@@ -2457,25 +2643,25 @@ async function installClaudeHooksAtPath(settingsPath) {
2457
2643
  created = true;
2458
2644
  }
2459
2645
  const patched = patchClaudeSettings(raw);
2460
- await mkdir5(path8.dirname(settingsPath), { recursive: true });
2461
- await writeFile4(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
2646
+ await mkdir6(path9.dirname(settingsPath), { recursive: true });
2647
+ await writeFile5(settingsPath, JSON.stringify(patched, null, 2) + "\n", "utf8");
2462
2648
  return { settingsPath, created };
2463
2649
  }
2464
2650
  async function uninstallClaudeHooksAtPath(settingsPath) {
2465
- if (!existsSync7(settingsPath)) {
2651
+ if (!existsSync8(settingsPath)) {
2466
2652
  return { settingsPath, created: false };
2467
2653
  }
2468
2654
  const raw = JSON.parse(await readFile5(settingsPath, "utf8"));
2469
2655
  const cleaned = unpatchClaudeSettings(raw);
2470
- await writeFile4(settingsPath, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
2656
+ await writeFile5(settingsPath, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
2471
2657
  return { settingsPath, created: false };
2472
2658
  }
2473
2659
  function defaultClaudeSettingsPath(scope, projectRoot) {
2474
2660
  if (scope === "user") {
2475
2661
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
2476
- return path8.join(home, ".claude", "settings.json");
2662
+ return path9.join(home, ".claude", "settings.json");
2477
2663
  }
2478
- return path8.join(projectRoot, ".claude", "settings.local.json");
2664
+ return path9.join(projectRoot, ".claude", "settings.local.json");
2479
2665
  }
2480
2666
 
2481
2667
  // src/commands/install-hooks.ts
@@ -2531,20 +2717,20 @@ fi
2531
2717
  }
2532
2718
  ];
2533
2719
  async function installGitHooks(opts) {
2534
- const root = findProjectRoot6(opts.dir);
2535
- const gitDir = path9.join(root, ".git");
2536
- if (!existsSync8(gitDir)) {
2720
+ const root = findProjectRoot7(opts.dir);
2721
+ const gitDir = path10.join(root, ".git");
2722
+ if (!existsSync9(gitDir)) {
2537
2723
  ui.error(`No .git directory at ${root}.`);
2538
2724
  process.exitCode = 1;
2539
2725
  return;
2540
2726
  }
2541
- const hooksDir = path9.join(gitDir, "hooks");
2542
- await mkdir6(hooksDir, { recursive: true });
2727
+ const hooksDir = path10.join(gitDir, "hooks");
2728
+ await mkdir7(hooksDir, { recursive: true });
2543
2729
  let installed = 0;
2544
2730
  let skipped = 0;
2545
2731
  for (const { name, body } of HOOKS) {
2546
- const file = path9.join(hooksDir, name);
2547
- if (existsSync8(file) && !opts.force) {
2732
+ const file = path10.join(hooksDir, name);
2733
+ if (existsSync9(file) && !opts.force) {
2548
2734
  const existing = await readFile6(file, "utf8");
2549
2735
  if (!existing.includes(HOOK_MARKER)) {
2550
2736
  ui.warn(`${name} already exists and was not written by hAIve. Re-run with --force to overwrite.`);
@@ -2552,7 +2738,7 @@ async function installGitHooks(opts) {
2552
2738
  continue;
2553
2739
  }
2554
2740
  }
2555
- await writeFile5(file, body, "utf8");
2741
+ await writeFile6(file, body, "utf8");
2556
2742
  await chmod(file, 493);
2557
2743
  installed++;
2558
2744
  }
@@ -2562,7 +2748,7 @@ async function installGitHooks(opts) {
2562
2748
  ui.info("pre-push: haive enforce check blocks pushes that bypass briefing/session recap policy.");
2563
2749
  }
2564
2750
  async function installClaudeHooks(opts) {
2565
- const root = findProjectRoot6(opts.dir);
2751
+ const root = findProjectRoot7(opts.dir);
2566
2752
  const scope = opts.scope ?? "user";
2567
2753
  const settingsPath = opts.settings ?? defaultClaudeSettingsPath(scope, root);
2568
2754
  if (opts.uninstall) {
@@ -2607,11 +2793,11 @@ function registerInstallHooks(program2) {
2607
2793
  }
2608
2794
 
2609
2795
  // src/commands/observe.ts
2610
- import { appendFile, mkdir as mkdir7 } from "fs/promises";
2611
- import { existsSync as existsSync9 } from "fs";
2612
- import path10 from "path";
2796
+ import { appendFile, mkdir as mkdir8 } from "fs/promises";
2797
+ import { existsSync as existsSync10 } from "fs";
2798
+ import path11 from "path";
2613
2799
  import "commander";
2614
- import { findProjectRoot as findProjectRoot7, resolveHaivePaths as resolveHaivePaths5 } from "@hiveai/core";
2800
+ import { findProjectRoot as findProjectRoot8, resolveHaivePaths as resolveHaivePaths6 } from "@hiveai/core";
2615
2801
  var MAX_STDIN_BYTES = 256 * 1024;
2616
2802
  var TRUNCATE_FIELD = 800;
2617
2803
  function truncate(s, max = TRUNCATE_FIELD) {
@@ -2681,14 +2867,14 @@ function registerObserve(program2) {
2681
2867
  }
2682
2868
  const root = (() => {
2683
2869
  try {
2684
- return findProjectRoot7(opts.dir ?? payload.cwd);
2870
+ return findProjectRoot8(opts.dir ?? payload.cwd);
2685
2871
  } catch {
2686
2872
  return null;
2687
2873
  }
2688
2874
  })();
2689
2875
  if (!root) return;
2690
- const paths = resolveHaivePaths5(root);
2691
- if (!existsSync9(paths.haiveDir)) return;
2876
+ const paths = resolveHaivePaths6(root);
2877
+ if (!existsSync10(paths.haiveDir)) return;
2692
2878
  const observation = {
2693
2879
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2694
2880
  session_id: payload.session_id,
@@ -2697,10 +2883,10 @@ function registerObserve(program2) {
2697
2883
  summary: buildSummary(payload),
2698
2884
  files: extractFiles(payload)
2699
2885
  };
2700
- const cacheDir = path10.join(paths.haiveDir, ".cache");
2701
- await mkdir7(cacheDir, { recursive: true });
2886
+ const cacheDir = path11.join(paths.haiveDir, ".cache");
2887
+ await mkdir8(cacheDir, { recursive: true });
2702
2888
  await appendFile(
2703
- path10.join(cacheDir, "observations.jsonl"),
2889
+ path11.join(cacheDir, "observations.jsonl"),
2704
2890
  JSON.stringify(observation) + "\n",
2705
2891
  "utf8"
2706
2892
  );
@@ -2711,15 +2897,15 @@ function registerObserve(program2) {
2711
2897
 
2712
2898
  // src/commands/mcp.ts
2713
2899
  import "commander";
2714
- import { findProjectRoot as findProjectRoot9 } from "@hiveai/core";
2900
+ import { findProjectRoot as findProjectRoot10 } from "@hiveai/core";
2715
2901
 
2716
2902
  // ../mcp/dist/server.js
2717
2903
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2718
2904
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2719
- import { findProjectRoot as findProjectRoot8, resolveHaivePaths as resolveHaivePaths6 } from "@hiveai/core";
2720
- import { mkdir as mkdir8, writeFile as writeFile6 } from "fs/promises";
2721
- import { existsSync as existsSync10 } from "fs";
2722
- import path11 from "path";
2905
+ import { findProjectRoot as findProjectRoot9, resolveHaivePaths as resolveHaivePaths7 } from "@hiveai/core";
2906
+ import { mkdir as mkdir9, writeFile as writeFile7 } from "fs/promises";
2907
+ import { existsSync as existsSync11 } from "fs";
2908
+ import path12 from "path";
2723
2909
  import { z } from "zod";
2724
2910
  import { readFile as readFile7, readdir as readdir2 } from "fs/promises";
2725
2911
  import { existsSync as existsSync22 } from "fs";
@@ -2803,7 +2989,7 @@ import {
2803
2989
  } from "@hiveai/core";
2804
2990
  import { z as z10 } from "zod";
2805
2991
  import { writeFile as writeFile52 } from "fs/promises";
2806
- import { existsSync as existsSync11 } from "fs";
2992
+ import { existsSync as existsSync112 } from "fs";
2807
2993
  import { loadMemoriesFromDir as loadMemoriesFromDir9, serializeMemory as serializeMemory4 } from "@hiveai/core";
2808
2994
  import { z as z11 } from "zod";
2809
2995
  import { existsSync as existsSync12 } from "fs";
@@ -2820,7 +3006,7 @@ import {
2820
3006
  serializeMemory as serializeMemory5
2821
3007
  } from "@hiveai/core";
2822
3008
  import { z as z13 } from "zod";
2823
- import { mkdir as mkdir32, writeFile as writeFile7 } from "fs/promises";
3009
+ import { mkdir as mkdir32, writeFile as writeFile72 } from "fs/promises";
2824
3010
  import { existsSync as existsSync14 } from "fs";
2825
3011
  import path52 from "path";
2826
3012
  import {
@@ -2981,8 +3167,8 @@ import { loadConfigSync } from "@hiveai/core";
2981
3167
  function createContext(options = {}) {
2982
3168
  const env = options.env ?? process.env;
2983
3169
  const cwd = options.cwd ?? process.cwd();
2984
- const root = options.root ?? env.HAIVE_PROJECT_ROOT ?? findProjectRoot8(cwd);
2985
- return { paths: resolveHaivePaths6(root) };
3170
+ const root = options.root ?? env.HAIVE_PROJECT_ROOT ?? findProjectRoot9(cwd);
3171
+ return { paths: resolveHaivePaths7(root) };
2986
3172
  }
2987
3173
  var BootstrapProjectSaveInputSchema = {
2988
3174
  content: z.string().min(1).describe("Full Markdown content for the project (or module) context file"),
@@ -2992,15 +3178,15 @@ var BootstrapProjectSaveInputSchema = {
2992
3178
  overwrite: z.boolean().default(false).describe("Overwrite an existing file instead of failing")
2993
3179
  };
2994
3180
  async function bootstrapProjectSave(input, ctx) {
2995
- const target = input.module ? path11.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
2996
- const exists = existsSync10(target);
3181
+ const target = input.module ? path12.join(ctx.paths.modulesContextDir, input.module, "context.md") : ctx.paths.projectContext;
3182
+ const exists = existsSync11(target);
2997
3183
  if (exists && !input.overwrite) {
2998
3184
  throw new Error(
2999
3185
  `${target} already exists. Pass overwrite=true to replace it.`
3000
3186
  );
3001
3187
  }
3002
- await mkdir8(path11.dirname(target), { recursive: true });
3003
- await writeFile6(target, input.content, "utf8");
3188
+ await mkdir9(path12.dirname(target), { recursive: true });
3189
+ await writeFile7(target, input.content, "utf8");
3004
3190
  return {
3005
3191
  file_path: target,
3006
3192
  action: exists ? "overwritten" : "created"
@@ -3754,7 +3940,7 @@ var MemUpdateInputSchema = {
3754
3940
  author: z11.string().optional().describe("New author handle or email")
3755
3941
  };
3756
3942
  async function memUpdate(input, ctx) {
3757
- if (!existsSync11(ctx.paths.memoriesDir)) {
3943
+ if (!existsSync112(ctx.paths.memoriesDir)) {
3758
3944
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
3759
3945
  }
3760
3946
  const memories = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
@@ -3890,7 +4076,7 @@ async function memTried(input, ctx) {
3890
4076
  if (existsSync14(file)) {
3891
4077
  throw new Error(`Memory already exists at ${file}`);
3892
4078
  }
3893
- await writeFile7(file, serializeMemory6({ frontmatter, body }), "utf8");
4079
+ await writeFile72(file, serializeMemory6({ frontmatter, body }), "utf8");
3894
4080
  return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
3895
4081
  }
3896
4082
  var MemObserveInputSchema = {
@@ -4120,6 +4306,7 @@ async function memSessionEnd(input, ctx) {
4120
4306
  const revisionCount = (fm.revision_count ?? 0) + 1;
4121
4307
  const newFrontmatter = {
4122
4308
  ...fm,
4309
+ verified_at: (/* @__PURE__ */ new Date()).toISOString(),
4123
4310
  revision_count: revisionCount,
4124
4311
  anchor: {
4125
4312
  ...fm.anchor,
@@ -5520,13 +5707,11 @@ async function preCommitCheck(input, ctx) {
5520
5707
  const filesTouching = new Set(relevantMatches.map((m) => m.id));
5521
5708
  const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
5522
5709
  const blockOn = input.block_on;
5710
+ const blockingWarnings = apResult.warnings.filter(isBlockingWarning);
5523
5711
  let should_block = false;
5524
5712
  if (blockOn !== "never") {
5525
- const high = apResult.warnings.filter(
5526
- (w) => w.confidence === "authoritative" || w.confidence === "trusted"
5527
- );
5528
5713
  if (blockOn === "any" && (apResult.warnings.length > 0 || staleHits.length > 0)) should_block = true;
5529
- if (blockOn === "high-confidence" && (high.length > 0 || staleHits.length > 0)) should_block = true;
5714
+ if (blockOn === "high-confidence" && (blockingWarnings.length > 0 || staleHits.length > 0)) should_block = true;
5530
5715
  }
5531
5716
  const relevant_memories = relevantMatches.slice(0, 8).map((m) => ({
5532
5717
  id: m.id,
@@ -5538,6 +5723,7 @@ async function preCommitCheck(input, ctx) {
5538
5723
  should_block,
5539
5724
  summary: {
5540
5725
  anti_patterns: apResult.warnings.length,
5726
+ blocking_warnings: blockingWarnings.length,
5541
5727
  relevant_memories: relevant_memories.length,
5542
5728
  stale_anchors: staleHits.length
5543
5729
  },
@@ -5551,6 +5737,11 @@ async function preCommitCheck(input, ctx) {
5551
5737
  }))
5552
5738
  };
5553
5739
  }
5740
+ function isBlockingWarning(warning) {
5741
+ const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
5742
+ if (!highConfidence) return false;
5743
+ return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
5744
+ }
5554
5745
  var CONFIG_PATTERNS = [
5555
5746
  ".eslintrc",
5556
5747
  "eslint.config",
@@ -6082,7 +6273,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
6082
6273
  };
6083
6274
  }
6084
6275
  var SERVER_NAME = "haive";
6085
- var SERVER_VERSION = "0.9.11";
6276
+ var SERVER_VERSION = "0.9.13";
6086
6277
  function jsonResult(data) {
6087
6278
  return {
6088
6279
  content: [
@@ -6991,7 +7182,7 @@ function registerMcp(program2) {
6991
7182
  ).option("-d, --dir <dir>", "project root (walks up from here for .ai/ / .git/)").option("-r, --root <dir>", "same as --dir (parity with legacy haive-mcp --root)").option("--stdio", "optional marker for client configs \u2014 transport is always stdio", false).action(async (opts) => {
6992
7183
  void opts.stdio;
6993
7184
  const raw = opts.root ?? opts.dir;
6994
- const root = raw ? findProjectRoot9(raw) : findProjectRoot9();
7185
+ const root = raw ? findProjectRoot10(raw) : findProjectRoot10();
6995
7186
  try {
6996
7187
  await runHaiveMcpStdio({ root });
6997
7188
  } catch (err) {
@@ -7002,15 +7193,15 @@ function registerMcp(program2) {
7002
7193
  }
7003
7194
 
7004
7195
  // src/commands/sync.ts
7005
- import { spawnSync as spawnSync2 } from "child_process";
7006
- import { readFile as readFile8, writeFile as writeFile13, mkdir as mkdir9 } from "fs/promises";
7196
+ import { spawnSync as spawnSync3 } from "child_process";
7197
+ import { readFile as readFile8, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
7007
7198
  import { existsSync as existsSync29 } from "fs";
7008
- import path12 from "path";
7199
+ import path13 from "path";
7009
7200
  import "commander";
7010
7201
  import {
7011
7202
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
7012
7203
  buildFrontmatter as buildFrontmatter6,
7013
- findProjectRoot as findProjectRoot10,
7204
+ findProjectRoot as findProjectRoot11,
7014
7205
  getUsage as getUsage10,
7015
7206
  isAutoPromoteEligible as isAutoPromoteEligible2,
7016
7207
  isDecaying as isDecaying2,
@@ -7019,7 +7210,7 @@ import {
7019
7210
  loadMemoriesFromDir as loadMemoriesFromDir23,
7020
7211
  loadUsageIndex as loadUsageIndex12,
7021
7212
  pullCrossRepoSources,
7022
- resolveHaivePaths as resolveHaivePaths7,
7213
+ resolveHaivePaths as resolveHaivePaths8,
7023
7214
  resolveManifestFiles,
7024
7215
  serializeMemory as serializeMemory11,
7025
7216
  trackDependencies,
@@ -7038,8 +7229,8 @@ function registerSync(program2) {
7038
7229
  "--inject-bridge",
7039
7230
  "inject top validated memories into CLAUDE.md (or --bridge-file) between <!-- haive:memories-start/end --> markers"
7040
7231
  ).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").action(async (opts) => {
7041
- const root = findProjectRoot10(opts.dir);
7042
- const paths = resolveHaivePaths7(root);
7232
+ const root = findProjectRoot11(opts.dir);
7233
+ const paths = resolveHaivePaths8(root);
7043
7234
  if (!existsSync29(paths.memoriesDir)) {
7044
7235
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
7045
7236
  process.exitCode = 1;
@@ -7172,7 +7363,7 @@ function registerSync(program2) {
7172
7363
  );
7173
7364
  }
7174
7365
  if (opts.injectBridge) {
7175
- const bridgeFile = opts.bridgeFile ? path12.resolve(opts.bridgeFile) : path12.join(root, "CLAUDE.md");
7366
+ const bridgeFile = opts.bridgeFile ? path13.resolve(opts.bridgeFile) : path13.join(root, "CLAUDE.md");
7176
7367
  const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
7177
7368
  await injectBridge(bridgeFile, paths.memoriesDir, maxInject, root, opts.quiet);
7178
7369
  }
@@ -7278,10 +7469,10 @@ Attends une **confirmation explicite** avant d'agir.
7278
7469
  paths: [result.file],
7279
7470
  topic: `dep-bump-${slugParts}`
7280
7471
  });
7281
- const teamDir = path12.join(paths.memoriesDir, "team");
7282
- await mkdir9(teamDir, { recursive: true });
7472
+ const teamDir = path13.join(paths.memoriesDir, "team");
7473
+ await mkdir10(teamDir, { recursive: true });
7283
7474
  await writeFile13(
7284
- path12.join(teamDir, `${fm.id}.md`),
7475
+ path13.join(teamDir, `${fm.id}.md`),
7285
7476
  serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
7286
7477
  "utf8"
7287
7478
  );
@@ -7345,10 +7536,10 @@ Attends une **confirmation explicite** avant d'agir.
7345
7536
  paths: [diff.file],
7346
7537
  topic: `contract-breaking-${diff.contract}`
7347
7538
  });
7348
- const teamDir = path12.join(paths.memoriesDir, "team");
7349
- await mkdir9(teamDir, { recursive: true });
7539
+ const teamDir = path13.join(paths.memoriesDir, "team");
7540
+ await mkdir10(teamDir, { recursive: true });
7350
7541
  await writeFile13(
7351
- path12.join(teamDir, `${fm.id}.md`),
7542
+ path13.join(teamDir, `${fm.id}.md`),
7352
7543
  serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
7353
7544
  "utf8"
7354
7545
  );
@@ -7362,7 +7553,7 @@ Attends une **confirmation explicite** avant d'agir.
7362
7553
  const existingMap = await loadCodeMap4(paths);
7363
7554
  if (existingMap) {
7364
7555
  const mapAge = new Date(existingMap.generated_at).getTime();
7365
- const gitResult = spawnSync2(
7556
+ const gitResult = spawnSync3(
7366
7557
  "git",
7367
7558
  [
7368
7559
  "diff",
@@ -7441,11 +7632,11 @@ ${BRIDGE_END}`;
7441
7632
  const startIdx = existing.indexOf(BRIDGE_START);
7442
7633
  const endIdx = existing.indexOf(BRIDGE_END);
7443
7634
  if (startIdx !== -1 && endIdx === -1) {
7444
- ui.warn(`${path12.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
7635
+ ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
7445
7636
  return;
7446
7637
  }
7447
7638
  if (startIdx === -1 && endIdx !== -1) {
7448
- ui.warn(`${path12.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
7639
+ ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
7449
7640
  return;
7450
7641
  }
7451
7642
  let updated;
@@ -7453,19 +7644,19 @@ ${BRIDGE_END}`;
7453
7644
  updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
7454
7645
  } else {
7455
7646
  if (!fileExists && !quiet) {
7456
- ui.info(`Creating ${path12.relative(root, bridgeFile)} with haive memory block.`);
7647
+ ui.info(`Creating ${path13.relative(root, bridgeFile)} with haive memory block.`);
7457
7648
  }
7458
7649
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
7459
7650
  }
7460
7651
  await writeFile13(bridgeFile, updated, "utf8");
7461
7652
  if (!quiet) {
7462
7653
  console.log(
7463
- ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path12.relative(root, bridgeFile)}`)
7654
+ ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path13.relative(root, bridgeFile)}`)
7464
7655
  );
7465
7656
  }
7466
7657
  }
7467
7658
  function collectSinceChanges(root, ref) {
7468
- const result = spawnSync2(
7659
+ const result = spawnSync3(
7469
7660
  "git",
7470
7661
  ["-C", root, "diff", "--name-status", "--diff-filter=AMD", `${ref}...HEAD`, "--", ".ai/memories"],
7471
7662
  { encoding: "utf8" }
@@ -7485,17 +7676,17 @@ function collectSinceChanges(root, ref) {
7485
7676
 
7486
7677
  // src/commands/memory-add.ts
7487
7678
  import { createHash as createHash2 } from "crypto";
7488
- import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
7679
+ import { mkdir as mkdir11, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
7489
7680
  import { existsSync as existsSync30 } from "fs";
7490
- import path13 from "path";
7681
+ import path14 from "path";
7491
7682
  import "commander";
7492
7683
  import {
7493
7684
  buildFrontmatter as buildFrontmatter7,
7494
- findProjectRoot as findProjectRoot11,
7685
+ findProjectRoot as findProjectRoot12,
7495
7686
  inferModulesFromPaths as inferModulesFromPaths3,
7496
7687
  loadMemoriesFromDir as loadMemoriesFromDir24,
7497
7688
  memoryFilePath as memoryFilePath6,
7498
- resolveHaivePaths as resolveHaivePaths8,
7689
+ resolveHaivePaths as resolveHaivePaths9,
7499
7690
  serializeMemory as serializeMemory12
7500
7691
  } from "@hiveai/core";
7501
7692
  function registerMemoryAdd(memory2) {
@@ -7523,8 +7714,8 @@ function registerMemoryAdd(memory2) {
7523
7714
  --scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
7524
7715
  `
7525
7716
  ).requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: personal, or team in autopilot)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("-d, --dir <dir>", "project root").action(async (opts) => {
7526
- const root = findProjectRoot11(opts.dir);
7527
- const paths = resolveHaivePaths8(root);
7717
+ const root = findProjectRoot12(opts.dir);
7718
+ const paths = resolveHaivePaths9(root);
7528
7719
  if (!existsSync30(paths.haiveDir)) {
7529
7720
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
7530
7721
  process.exitCode = 1;
@@ -7536,7 +7727,7 @@ function registerMemoryAdd(memory2) {
7536
7727
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
7537
7728
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
7538
7729
  if (anchorPaths.length > 0) {
7539
- const missing = anchorPaths.filter((p) => !existsSync30(path13.resolve(root, p)));
7730
+ const missing = anchorPaths.filter((p) => !existsSync30(path14.resolve(root, p)));
7540
7731
  if (missing.length > 0) {
7541
7732
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
7542
7733
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -7601,7 +7792,7 @@ TODO \u2014 write the memory body.
7601
7792
  }
7602
7793
  };
7603
7794
  await writeFile14(topicMatch.filePath, serializeMemory12({ frontmatter: newFrontmatter, body }), "utf8");
7604
- ui.success(`Updated (topic upsert) ${path13.relative(root, topicMatch.filePath)}`);
7795
+ ui.success(`Updated (topic upsert) ${path14.relative(root, topicMatch.filePath)}`);
7605
7796
  ui.info(`id=${fm.id} revision=${revisionCount}`);
7606
7797
  return;
7607
7798
  }
@@ -7620,7 +7811,7 @@ TODO \u2014 write the memory body.
7620
7811
  topic: opts.topic
7621
7812
  });
7622
7813
  const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
7623
- await mkdir10(path13.dirname(file), { recursive: true });
7814
+ await mkdir11(path14.dirname(file), { recursive: true });
7624
7815
  if (existsSync30(file)) {
7625
7816
  ui.error(`Memory already exists at ${file}`);
7626
7817
  process.exitCode = 1;
@@ -7639,7 +7830,7 @@ TODO \u2014 write the memory body.
7639
7830
  }
7640
7831
  }
7641
7832
  await writeFile14(file, serializeMemory12({ frontmatter, body }), "utf8");
7642
- ui.success(`Created ${path13.relative(root, file)}`);
7833
+ ui.success(`Created ${path14.relative(root, file)}`);
7643
7834
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
7644
7835
  if (inferredTags.length > 0) {
7645
7836
  ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
@@ -7670,9 +7861,9 @@ function parseCsv2(value) {
7670
7861
 
7671
7862
  // src/commands/memory-list.ts
7672
7863
  import { existsSync as existsSync31 } from "fs";
7673
- import path14 from "path";
7864
+ import path15 from "path";
7674
7865
  import "commander";
7675
- import { findProjectRoot as findProjectRoot12, resolveHaivePaths as resolveHaivePaths9 } from "@hiveai/core";
7866
+ import { findProjectRoot as findProjectRoot13, resolveHaivePaths as resolveHaivePaths10 } from "@hiveai/core";
7676
7867
 
7677
7868
  // src/utils/fs.ts
7678
7869
  import {
@@ -7684,8 +7875,8 @@ import {
7684
7875
  // src/commands/memory-list.ts
7685
7876
  function registerMemoryList(memory2) {
7686
7877
  memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("-d, --dir <dir>", "project root").action(async (opts) => {
7687
- const root = findProjectRoot12(opts.dir);
7688
- const paths = resolveHaivePaths9(root);
7878
+ const root = findProjectRoot13(opts.dir);
7879
+ const paths = resolveHaivePaths10(root);
7689
7880
  if (!existsSync31(paths.memoriesDir)) {
7690
7881
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
7691
7882
  process.exitCode = 1;
@@ -7718,7 +7909,7 @@ function registerMemoryList(memory2) {
7718
7909
  console.log(
7719
7910
  `${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
7720
7911
  );
7721
- console.log(` ${ui.dim(path14.relative(root, filePath))}`);
7912
+ console.log(` ${ui.dim(path15.relative(root, filePath))}`);
7722
7913
  }
7723
7914
  console.log(ui.dim(`
7724
7915
  ${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
@@ -7753,20 +7944,20 @@ function matchesFilters(loaded, opts) {
7753
7944
  }
7754
7945
 
7755
7946
  // src/commands/memory-promote.ts
7756
- import { mkdir as mkdir11, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
7947
+ import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
7757
7948
  import { existsSync as existsSync33 } from "fs";
7758
- import path15 from "path";
7949
+ import path16 from "path";
7759
7950
  import "commander";
7760
7951
  import {
7761
- findProjectRoot as findProjectRoot13,
7952
+ findProjectRoot as findProjectRoot14,
7762
7953
  memoryFilePath as memoryFilePath7,
7763
- resolveHaivePaths as resolveHaivePaths10,
7954
+ resolveHaivePaths as resolveHaivePaths11,
7764
7955
  serializeMemory as serializeMemory13
7765
7956
  } from "@hiveai/core";
7766
7957
  function registerMemoryPromote(memory2) {
7767
7958
  memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
7768
- const root = findProjectRoot13(opts.dir);
7769
- const paths = resolveHaivePaths10(root);
7959
+ const root = findProjectRoot14(opts.dir);
7960
+ const paths = resolveHaivePaths11(root);
7770
7961
  if (!existsSync33(paths.memoriesDir)) {
7771
7962
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
7772
7963
  process.exitCode = 1;
@@ -7802,11 +7993,11 @@ function registerMemoryPromote(memory2) {
7802
7993
  body: found.memory.body
7803
7994
  };
7804
7995
  const newPath = memoryFilePath7(paths, "team", updated.frontmatter.id);
7805
- await mkdir11(path15.dirname(newPath), { recursive: true });
7996
+ await mkdir12(path16.dirname(newPath), { recursive: true });
7806
7997
  await writeFile15(newPath, serializeMemory13(updated), "utf8");
7807
7998
  await unlink2(found.filePath);
7808
7999
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
7809
- ui.info(`Now at ${path15.relative(root, newPath)}`);
8000
+ ui.info(`Now at ${path16.relative(root, newPath)}`);
7810
8001
  console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
7811
8002
  });
7812
8003
  }
@@ -7814,17 +8005,17 @@ function registerMemoryPromote(memory2) {
7814
8005
  // src/commands/memory-approve.ts
7815
8006
  import { existsSync as existsSync34 } from "fs";
7816
8007
  import { writeFile as writeFile16 } from "fs/promises";
7817
- import path16 from "path";
8008
+ import path17 from "path";
7818
8009
  import "commander";
7819
8010
  import {
7820
- findProjectRoot as findProjectRoot14,
7821
- resolveHaivePaths as resolveHaivePaths11,
8011
+ findProjectRoot as findProjectRoot15,
8012
+ resolveHaivePaths as resolveHaivePaths12,
7822
8013
  serializeMemory as serializeMemory14
7823
8014
  } from "@hiveai/core";
7824
8015
  function registerMemoryApprove(memory2) {
7825
8016
  memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
7826
- const root = findProjectRoot14(opts.dir);
7827
- const paths = resolveHaivePaths11(root);
8017
+ const root = findProjectRoot15(opts.dir);
8018
+ const paths = resolveHaivePaths12(root);
7828
8019
  if (!existsSync34(paths.memoriesDir)) {
7829
8020
  ui.error(`No .ai/memories at ${root}.`);
7830
8021
  process.exitCode = 1;
@@ -7878,24 +8069,24 @@ function registerMemoryApprove(memory2) {
7878
8069
  };
7879
8070
  await writeFile16(found.filePath, serializeMemory14(next), "utf8");
7880
8071
  ui.success(`Approved ${id} (status=validated)`);
7881
- ui.info(path16.relative(root, found.filePath));
8072
+ ui.info(path17.relative(root, found.filePath));
7882
8073
  });
7883
8074
  }
7884
8075
 
7885
8076
  // src/commands/memory-update.ts
7886
8077
  import { writeFile as writeFile17 } from "fs/promises";
7887
8078
  import { existsSync as existsSync35 } from "fs";
7888
- import path17 from "path";
8079
+ import path18 from "path";
7889
8080
  import "commander";
7890
8081
  import {
7891
- findProjectRoot as findProjectRoot15,
7892
- resolveHaivePaths as resolveHaivePaths12,
8082
+ findProjectRoot as findProjectRoot16,
8083
+ resolveHaivePaths as resolveHaivePaths13,
7893
8084
  serializeMemory as serializeMemory15
7894
8085
  } from "@hiveai/core";
7895
8086
  function registerMemoryUpdate(memory2) {
7896
8087
  memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
7897
- const root = findProjectRoot15(opts.dir);
7898
- const paths = resolveHaivePaths12(root);
8088
+ const root = findProjectRoot16(opts.dir);
8089
+ const paths = resolveHaivePaths13(root);
7899
8090
  if (!existsSync35(paths.memoriesDir)) {
7900
8091
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
7901
8092
  process.exitCode = 1;
@@ -7948,7 +8139,7 @@ function registerMemoryUpdate(memory2) {
7948
8139
  serializeMemory15({ frontmatter: newFrontmatter, body: newBody }),
7949
8140
  "utf8"
7950
8141
  );
7951
- ui.success(`Updated ${path17.relative(root, loaded.filePath)}`);
8142
+ ui.success(`Updated ${path18.relative(root, loaded.filePath)}`);
7952
8143
  ui.info(`fields: ${updated.join(", ")}`);
7953
8144
  });
7954
8145
  }
@@ -7969,15 +8160,15 @@ function parseCsv3(value) {
7969
8160
  // src/commands/memory-auto-promote.ts
7970
8161
  import { writeFile as writeFile18 } from "fs/promises";
7971
8162
  import { existsSync as existsSync36 } from "fs";
7972
- import path18 from "path";
8163
+ import path19 from "path";
7973
8164
  import "commander";
7974
8165
  import {
7975
8166
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
7976
- findProjectRoot as findProjectRoot16,
8167
+ findProjectRoot as findProjectRoot17,
7977
8168
  getUsage as getUsage11,
7978
8169
  isAutoPromoteEligible as isAutoPromoteEligible3,
7979
8170
  loadUsageIndex as loadUsageIndex13,
7980
- resolveHaivePaths as resolveHaivePaths13,
8171
+ resolveHaivePaths as resolveHaivePaths14,
7981
8172
  serializeMemory as serializeMemory16
7982
8173
  } from "@hiveai/core";
7983
8174
  function registerMemoryAutoPromote(memory2) {
@@ -7986,8 +8177,8 @@ function registerMemoryAutoPromote(memory2) {
7986
8177
  "memories with more rejections than this are skipped",
7987
8178
  String(DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
7988
8179
  ).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
7989
- const root = findProjectRoot16(opts.dir);
7990
- const paths = resolveHaivePaths13(root);
8180
+ const root = findProjectRoot17(opts.dir);
8181
+ const paths = resolveHaivePaths14(root);
7991
8182
  if (!existsSync36(paths.memoriesDir)) {
7992
8183
  ui.error(`No .ai/memories at ${root}.`);
7993
8184
  process.exitCode = 1;
@@ -8014,7 +8205,7 @@ function registerMemoryAutoPromote(memory2) {
8014
8205
  console.log(
8015
8206
  `${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
8016
8207
  );
8017
- console.log(` ${ui.dim(path18.relative(root, filePath))}`);
8208
+ console.log(` ${ui.dim(path19.relative(root, filePath))}`);
8018
8209
  if (opts.apply) {
8019
8210
  const next = {
8020
8211
  frontmatter: { ...mem.frontmatter, status: "validated" },
@@ -8033,17 +8224,17 @@ function registerMemoryAutoPromote(memory2) {
8033
8224
  import { spawn as spawn3 } from "child_process";
8034
8225
  import { existsSync as existsSync37 } from "fs";
8035
8226
  import { readFile as readFile10 } from "fs/promises";
8036
- import path19 from "path";
8227
+ import path20 from "path";
8037
8228
  import "commander";
8038
8229
  import {
8039
- findProjectRoot as findProjectRoot17,
8230
+ findProjectRoot as findProjectRoot18,
8040
8231
  parseMemory,
8041
- resolveHaivePaths as resolveHaivePaths14
8232
+ resolveHaivePaths as resolveHaivePaths15
8042
8233
  } from "@hiveai/core";
8043
8234
  function registerMemoryEdit(memory2) {
8044
8235
  memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
8045
- const root = findProjectRoot17(opts.dir);
8046
- const paths = resolveHaivePaths14(root);
8236
+ const root = findProjectRoot18(opts.dir);
8237
+ const paths = resolveHaivePaths15(root);
8047
8238
  if (!existsSync37(paths.memoriesDir)) {
8048
8239
  ui.error(`No .ai/memories at ${root}.`);
8049
8240
  process.exitCode = 1;
@@ -8057,7 +8248,7 @@ function registerMemoryEdit(memory2) {
8057
8248
  return;
8058
8249
  }
8059
8250
  const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
8060
- ui.info(`Opening ${path19.relative(root, found.filePath)} with ${editor}\u2026`);
8251
+ ui.info(`Opening ${path20.relative(root, found.filePath)} with ${editor}\u2026`);
8061
8252
  const code = await runEditor(editor, found.filePath);
8062
8253
  if (code !== 0) {
8063
8254
  ui.warn(`Editor exited with status ${code}.`);
@@ -8085,21 +8276,21 @@ function runEditor(editor, file) {
8085
8276
 
8086
8277
  // src/commands/memory-for-files.ts
8087
8278
  import { existsSync as existsSync38 } from "fs";
8088
- import path20 from "path";
8279
+ import path21 from "path";
8089
8280
  import "commander";
8090
8281
  import {
8091
8282
  deriveConfidence as deriveConfidence9,
8092
- findProjectRoot as findProjectRoot18,
8283
+ findProjectRoot as findProjectRoot19,
8093
8284
  getUsage as getUsage12,
8094
8285
  inferModulesFromPaths as inferModulesFromPaths4,
8095
8286
  loadUsageIndex as loadUsageIndex14,
8096
8287
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths5,
8097
- resolveHaivePaths as resolveHaivePaths15
8288
+ resolveHaivePaths as resolveHaivePaths16
8098
8289
  } from "@hiveai/core";
8099
8290
  function registerMemoryForFiles(memory2) {
8100
8291
  memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
8101
- const root = findProjectRoot18(opts.dir);
8102
- const paths = resolveHaivePaths15(root);
8292
+ const root = findProjectRoot19(opts.dir);
8293
+ const paths = resolveHaivePaths16(root);
8103
8294
  if (!existsSync38(paths.memoriesDir)) {
8104
8295
  ui.error(`No .ai/memories at ${root}.`);
8105
8296
  process.exitCode = 1;
@@ -8207,24 +8398,24 @@ function printGroup(root, label, loaded, usage) {
8207
8398
  const u = getUsage12(usage, fm.id);
8208
8399
  const conf = deriveConfidence9(fm, u);
8209
8400
  console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
8210
- console.log(` ${ui.dim(path20.relative(root, filePath))}`);
8401
+ console.log(` ${ui.dim(path21.relative(root, filePath))}`);
8211
8402
  }
8212
8403
  }
8213
8404
 
8214
8405
  // src/commands/memory-hot.ts
8215
8406
  import { existsSync as existsSync39 } from "fs";
8216
- import path21 from "path";
8407
+ import path23 from "path";
8217
8408
  import "commander";
8218
8409
  import {
8219
- findProjectRoot as findProjectRoot19,
8410
+ findProjectRoot as findProjectRoot20,
8220
8411
  getUsage as getUsage13,
8221
8412
  loadUsageIndex as loadUsageIndex15,
8222
- resolveHaivePaths as resolveHaivePaths16
8413
+ resolveHaivePaths as resolveHaivePaths17
8223
8414
  } from "@hiveai/core";
8224
8415
  function registerMemoryHot(memory2) {
8225
8416
  memory2.command("hot").description("List memories actively used but not yet validated (good promotion candidates)").option("--threshold <n>", "minimum read_count to qualify", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
8226
- const root = findProjectRoot19(opts.dir);
8227
- const paths = resolveHaivePaths16(root);
8417
+ const root = findProjectRoot20(opts.dir);
8418
+ const paths = resolveHaivePaths17(root);
8228
8419
  if (!existsSync39(paths.memoriesDir)) {
8229
8420
  ui.error(`No .ai/memories at ${root}.`);
8230
8421
  process.exitCode = 1;
@@ -8253,7 +8444,7 @@ function registerMemoryHot(memory2) {
8253
8444
  console.log(
8254
8445
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(fm.status)} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
8255
8446
  );
8256
- console.log(` ${ui.dim(path21.relative(root, filePath))}`);
8447
+ console.log(` ${ui.dim(path23.relative(root, filePath))}`);
8257
8448
  }
8258
8449
  ui.info(
8259
8450
  `${candidates.length} hot \u2014 promote drafts with \`haive memory promote <id>\`, then \`haive memory auto-promote --apply\`.`
@@ -8262,15 +8453,15 @@ function registerMemoryHot(memory2) {
8262
8453
  }
8263
8454
 
8264
8455
  // src/commands/memory-tried.ts
8265
- import { mkdir as mkdir12, writeFile as writeFile19 } from "fs/promises";
8456
+ import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
8266
8457
  import { existsSync as existsSync40 } from "fs";
8267
- import path23 from "path";
8458
+ import path24 from "path";
8268
8459
  import "commander";
8269
8460
  import {
8270
8461
  buildFrontmatter as buildFrontmatter8,
8271
- findProjectRoot as findProjectRoot20,
8462
+ findProjectRoot as findProjectRoot21,
8272
8463
  memoryFilePath as memoryFilePath8,
8273
- resolveHaivePaths as resolveHaivePaths17,
8464
+ resolveHaivePaths as resolveHaivePaths18,
8274
8465
  serializeMemory as serializeMemory17
8275
8466
  } from "@hiveai/core";
8276
8467
  function registerMemoryTried(memory2) {
@@ -8290,8 +8481,8 @@ function registerMemoryTried(memory2) {
8290
8481
  --paths packages/cli/src/index.ts
8291
8482
  `
8292
8483
  ).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
8293
- const root = findProjectRoot20(opts.dir);
8294
- const paths = resolveHaivePaths17(root);
8484
+ const root = findProjectRoot21(opts.dir);
8485
+ const paths = resolveHaivePaths18(root);
8295
8486
  if (!existsSync40(paths.haiveDir)) {
8296
8487
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
8297
8488
  process.exitCode = 1;
@@ -8315,14 +8506,14 @@ function registerMemoryTried(memory2) {
8315
8506
  }
8316
8507
  const body = lines.join("\n") + "\n";
8317
8508
  const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
8318
- await mkdir12(path23.dirname(file), { recursive: true });
8509
+ await mkdir13(path24.dirname(file), { recursive: true });
8319
8510
  if (existsSync40(file)) {
8320
8511
  ui.error(`Memory already exists at ${file}`);
8321
8512
  process.exitCode = 1;
8322
8513
  return;
8323
8514
  }
8324
8515
  await writeFile19(file, serializeMemory17({ frontmatter, body }), "utf8");
8325
- ui.success(`Recorded: ${path23.relative(root, file)}`);
8516
+ ui.success(`Recorded: ${path24.relative(root, file)}`);
8326
8517
  ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
8327
8518
  });
8328
8519
  }
@@ -8333,18 +8524,18 @@ function parseCsv4(value) {
8333
8524
 
8334
8525
  // src/commands/memory-pending.ts
8335
8526
  import { existsSync as existsSync41 } from "fs";
8336
- import path24 from "path";
8527
+ import path25 from "path";
8337
8528
  import "commander";
8338
8529
  import {
8339
- findProjectRoot as findProjectRoot21,
8530
+ findProjectRoot as findProjectRoot22,
8340
8531
  getUsage as getUsage14,
8341
8532
  loadUsageIndex as loadUsageIndex16,
8342
- resolveHaivePaths as resolveHaivePaths18
8533
+ resolveHaivePaths as resolveHaivePaths19
8343
8534
  } from "@hiveai/core";
8344
8535
  function registerMemoryPending(memory2) {
8345
8536
  memory2.command("pending").description("List 'proposed' memories awaiting review (sorted by reads desc)").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
8346
- const root = findProjectRoot21(opts.dir);
8347
- const paths = resolveHaivePaths18(root);
8537
+ const root = findProjectRoot22(opts.dir);
8538
+ const paths = resolveHaivePaths19(root);
8348
8539
  if (!existsSync41(paths.memoriesDir)) {
8349
8540
  ui.error(`No .ai/memories at ${root}.`);
8350
8541
  process.exitCode = 1;
@@ -8373,7 +8564,7 @@ function registerMemoryPending(memory2) {
8373
8564
  console.log(
8374
8565
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count} rejections=${u.rejected_count}`)}`
8375
8566
  );
8376
- console.log(` ${ui.dim(path24.relative(root, filePath))}`);
8567
+ console.log(` ${ui.dim(path25.relative(root, filePath))}`);
8377
8568
  }
8378
8569
  ui.info(`${proposed.length} pending`);
8379
8570
  });
@@ -8381,22 +8572,22 @@ function registerMemoryPending(memory2) {
8381
8572
 
8382
8573
  // src/commands/memory-query.ts
8383
8574
  import { existsSync as existsSync43 } from "fs";
8384
- import path25 from "path";
8575
+ import path26 from "path";
8385
8576
  import "commander";
8386
8577
  import {
8387
8578
  extractSnippet as extractSnippet2,
8388
- findProjectRoot as findProjectRoot22,
8579
+ findProjectRoot as findProjectRoot23,
8389
8580
  literalMatchesAllTokens as literalMatchesAllTokens3,
8390
8581
  literalMatchesAnyToken as literalMatchesAnyToken4,
8391
8582
  pickSnippetNeedle as pickSnippetNeedle2,
8392
- resolveHaivePaths as resolveHaivePaths19,
8583
+ resolveHaivePaths as resolveHaivePaths20,
8393
8584
  tokenizeQuery as tokenizeQuery6,
8394
8585
  trackReads as trackReads4
8395
8586
  } from "@hiveai/core";
8396
8587
  function registerMemoryQuery(memory2) {
8397
8588
  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) => {
8398
- const root = findProjectRoot22(opts.dir);
8399
- const paths = resolveHaivePaths19(root);
8589
+ const root = findProjectRoot23(opts.dir);
8590
+ const paths = resolveHaivePaths20(root);
8400
8591
  if (!existsSync43(paths.memoriesDir)) {
8401
8592
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
8402
8593
  process.exitCode = 1;
@@ -8438,7 +8629,7 @@ function registerMemoryQuery(memory2) {
8438
8629
  const fm = mem.frontmatter;
8439
8630
  const statusBadge = ui.statusBadge(fm.status);
8440
8631
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
8441
- console.log(` ${ui.dim(path25.relative(root, filePath))}`);
8632
+ console.log(` ${ui.dim(path26.relative(root, filePath))}`);
8442
8633
  const snippet = extractSnippet2(mem.body, snippetNeedle);
8443
8634
  if (snippet) console.log(` ${snippet}`);
8444
8635
  }
@@ -8459,17 +8650,17 @@ import { writeFile as writeFile20 } from "fs/promises";
8459
8650
  import { existsSync as existsSync44 } from "fs";
8460
8651
  import "commander";
8461
8652
  import {
8462
- findProjectRoot as findProjectRoot23,
8653
+ findProjectRoot as findProjectRoot24,
8463
8654
  loadUsageIndex as loadUsageIndex17,
8464
8655
  recordRejection as recordRejection2,
8465
- resolveHaivePaths as resolveHaivePaths20,
8656
+ resolveHaivePaths as resolveHaivePaths21,
8466
8657
  saveUsageIndex as saveUsageIndex3,
8467
8658
  serializeMemory as serializeMemory18
8468
8659
  } from "@hiveai/core";
8469
8660
  function registerMemoryReject(memory2) {
8470
8661
  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) => {
8471
- const root = findProjectRoot23(opts.dir);
8472
- const paths = resolveHaivePaths20(root);
8662
+ const root = findProjectRoot24(opts.dir);
8663
+ const paths = resolveHaivePaths21(root);
8473
8664
  if (!existsSync44(paths.memoriesDir)) {
8474
8665
  ui.error(`No .ai/memories at ${root}.`);
8475
8666
  process.exitCode = 1;
@@ -8508,19 +8699,19 @@ function registerMemoryReject(memory2) {
8508
8699
  // src/commands/memory-rm.ts
8509
8700
  import { existsSync as existsSync45 } from "fs";
8510
8701
  import { unlink as unlink3 } from "fs/promises";
8511
- import path26 from "path";
8512
- import { createInterface } from "readline/promises";
8702
+ import path27 from "path";
8703
+ import { createInterface as createInterface2 } from "readline/promises";
8513
8704
  import "commander";
8514
8705
  import {
8515
- findProjectRoot as findProjectRoot24,
8706
+ findProjectRoot as findProjectRoot25,
8516
8707
  loadUsageIndex as loadUsageIndex18,
8517
- resolveHaivePaths as resolveHaivePaths21,
8708
+ resolveHaivePaths as resolveHaivePaths22,
8518
8709
  saveUsageIndex as saveUsageIndex4
8519
8710
  } from "@hiveai/core";
8520
8711
  function registerMemoryRm(memory2) {
8521
8712
  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) => {
8522
- const root = findProjectRoot24(opts.dir);
8523
- const paths = resolveHaivePaths21(root);
8713
+ const root = findProjectRoot25(opts.dir);
8714
+ const paths = resolveHaivePaths22(root);
8524
8715
  if (!existsSync45(paths.memoriesDir)) {
8525
8716
  ui.error(`No .ai/memories at ${root}.`);
8526
8717
  process.exitCode = 1;
@@ -8533,9 +8724,9 @@ function registerMemoryRm(memory2) {
8533
8724
  process.exitCode = 1;
8534
8725
  return;
8535
8726
  }
8536
- const rel = path26.relative(root, found.filePath);
8727
+ const rel = path27.relative(root, found.filePath);
8537
8728
  if (!opts.yes) {
8538
- const rl = createInterface({ input: process.stdin, output: process.stdout });
8729
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
8539
8730
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
8540
8731
  rl.close();
8541
8732
  if (answer !== "y" && answer !== "yes") {
@@ -8559,19 +8750,19 @@ function registerMemoryRm(memory2) {
8559
8750
  // src/commands/memory-show.ts
8560
8751
  import { existsSync as existsSync46 } from "fs";
8561
8752
  import { readFile as readFile11 } from "fs/promises";
8562
- import path27 from "path";
8753
+ import path28 from "path";
8563
8754
  import "commander";
8564
8755
  import {
8565
8756
  deriveConfidence as deriveConfidence10,
8566
- findProjectRoot as findProjectRoot25,
8757
+ findProjectRoot as findProjectRoot26,
8567
8758
  getUsage as getUsage15,
8568
8759
  loadUsageIndex as loadUsageIndex19,
8569
- resolveHaivePaths as resolveHaivePaths22
8760
+ resolveHaivePaths as resolveHaivePaths23
8570
8761
  } from "@hiveai/core";
8571
8762
  function registerMemoryShow(memory2) {
8572
8763
  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) => {
8573
- const root = findProjectRoot25(opts.dir);
8574
- const paths = resolveHaivePaths22(root);
8764
+ const root = findProjectRoot26(opts.dir);
8765
+ const paths = resolveHaivePaths23(root);
8575
8766
  if (!existsSync46(paths.memoriesDir)) {
8576
8767
  ui.error(`No .ai/memories at ${root}.`);
8577
8768
  process.exitCode = 1;
@@ -8601,7 +8792,7 @@ function registerMemoryShow(memory2) {
8601
8792
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
8602
8793
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
8603
8794
  console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
8604
- console.log(`${ui.dim("file:")} ${path27.relative(root, found.filePath)}`);
8795
+ console.log(`${ui.dim("file:")} ${path28.relative(root, found.filePath)}`);
8605
8796
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
8606
8797
  console.log(ui.dim("anchor:"));
8607
8798
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -8617,19 +8808,19 @@ function registerMemoryShow(memory2) {
8617
8808
 
8618
8809
  // src/commands/memory-stats.ts
8619
8810
  import { existsSync as existsSync47 } from "fs";
8620
- import path28 from "path";
8811
+ import path29 from "path";
8621
8812
  import "commander";
8622
8813
  import {
8623
8814
  deriveConfidence as deriveConfidence11,
8624
- findProjectRoot as findProjectRoot26,
8815
+ findProjectRoot as findProjectRoot27,
8625
8816
  getUsage as getUsage16,
8626
8817
  loadUsageIndex as loadUsageIndex20,
8627
- resolveHaivePaths as resolveHaivePaths23
8818
+ resolveHaivePaths as resolveHaivePaths24
8628
8819
  } from "@hiveai/core";
8629
8820
  function registerMemoryStats(memory2) {
8630
8821
  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) => {
8631
- const root = findProjectRoot26(opts.dir);
8632
- const paths = resolveHaivePaths23(root);
8822
+ const root = findProjectRoot27(opts.dir);
8823
+ const paths = resolveHaivePaths24(root);
8633
8824
  if (!existsSync47(paths.memoriesDir)) {
8634
8825
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8635
8826
  process.exitCode = 1;
@@ -8655,7 +8846,7 @@ function registerMemoryStats(memory2) {
8655
8846
  console.log(
8656
8847
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
8657
8848
  );
8658
- console.log(` ${ui.dim(path28.relative(root, filePath))}`);
8849
+ console.log(` ${ui.dim(path29.relative(root, filePath))}`);
8659
8850
  }
8660
8851
  });
8661
8852
  }
@@ -8663,11 +8854,11 @@ function registerMemoryStats(memory2) {
8663
8854
  // src/commands/memory-verify.ts
8664
8855
  import { writeFile as writeFile21 } from "fs/promises";
8665
8856
  import { existsSync as existsSync48 } from "fs";
8666
- import path29 from "path";
8857
+ import path30 from "path";
8667
8858
  import "commander";
8668
8859
  import {
8669
- findProjectRoot as findProjectRoot27,
8670
- resolveHaivePaths as resolveHaivePaths24,
8860
+ findProjectRoot as findProjectRoot28,
8861
+ resolveHaivePaths as resolveHaivePaths25,
8671
8862
  serializeMemory as serializeMemory19,
8672
8863
  verifyAnchor as verifyAnchor3
8673
8864
  } from "@hiveai/core";
@@ -8675,8 +8866,8 @@ function registerMemoryVerify(memory2) {
8675
8866
  memory2.command("verify").description(
8676
8867
  "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"
8677
8868
  ).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) => {
8678
- const root = findProjectRoot27(opts.dir);
8679
- const paths = resolveHaivePaths24(root);
8869
+ const root = findProjectRoot28(opts.dir);
8870
+ const paths = resolveHaivePaths25(root);
8680
8871
  if (!existsSync48(paths.memoriesDir)) {
8681
8872
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8682
8873
  process.exitCode = 1;
@@ -8700,7 +8891,7 @@ function registerMemoryVerify(memory2) {
8700
8891
  anchorlessIds.push(mem.frontmatter.id);
8701
8892
  continue;
8702
8893
  }
8703
- const rel = path29.relative(root, filePath);
8894
+ const rel = path30.relative(root, filePath);
8704
8895
  if (result.stale) {
8705
8896
  staleCount++;
8706
8897
  console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
@@ -8767,15 +8958,15 @@ import { readFile as readFile12 } from "fs/promises";
8767
8958
  import { existsSync as existsSync49 } from "fs";
8768
8959
  import "commander";
8769
8960
  import {
8770
- findProjectRoot as findProjectRoot28,
8771
- resolveHaivePaths as resolveHaivePaths25
8961
+ findProjectRoot as findProjectRoot29,
8962
+ resolveHaivePaths as resolveHaivePaths26
8772
8963
  } from "@hiveai/core";
8773
8964
  function registerMemoryImport(memory2) {
8774
8965
  memory2.command("import").description(
8775
8966
  "Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
8776
8967
  ).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) => {
8777
- const root = findProjectRoot28(opts.dir);
8778
- const paths = resolveHaivePaths25(root);
8968
+ const root = findProjectRoot29(opts.dir);
8969
+ const paths = resolveHaivePaths26(root);
8779
8970
  if (!existsSync49(paths.haiveDir)) {
8780
8971
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
8781
8972
  process.exitCode = 1;
@@ -8815,13 +9006,13 @@ function registerMemoryImport(memory2) {
8815
9006
 
8816
9007
  // src/commands/memory-import-changelog.ts
8817
9008
  import { existsSync as existsSync50 } from "fs";
8818
- import { readFile as readFile13, mkdir as mkdir13, writeFile as writeFile23 } from "fs/promises";
8819
- import path30 from "path";
9009
+ import { readFile as readFile13, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
9010
+ import path31 from "path";
8820
9011
  import "commander";
8821
9012
  import {
8822
9013
  buildFrontmatter as buildFrontmatter9,
8823
- findProjectRoot as findProjectRoot29,
8824
- resolveHaivePaths as resolveHaivePaths26,
9014
+ findProjectRoot as findProjectRoot30,
9015
+ resolveHaivePaths as resolveHaivePaths27,
8825
9016
  serializeMemory as serializeMemory20
8826
9017
  } from "@hiveai/core";
8827
9018
  function parseChangelog(content) {
@@ -8886,9 +9077,9 @@ function registerMemoryImportChangelog(memory2) {
8886
9077
  "--versions <csv>",
8887
9078
  "only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
8888
9079
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
8889
- const root = findProjectRoot29(opts.dir);
8890
- const paths = resolveHaivePaths26(root);
8891
- const changelogPath = path30.resolve(root, opts.fromChangelog);
9080
+ const root = findProjectRoot30(opts.dir);
9081
+ const paths = resolveHaivePaths27(root);
9082
+ const changelogPath = path31.resolve(root, opts.fromChangelog);
8892
9083
  if (!existsSync50(changelogPath)) {
8893
9084
  ui.error(`CHANGELOG not found: ${changelogPath}`);
8894
9085
  process.exitCode = 1;
@@ -8909,10 +9100,10 @@ function registerMemoryImportChangelog(memory2) {
8909
9100
  entries = entries.filter((e) => requested.includes(e.version));
8910
9101
  }
8911
9102
  }
8912
- const pkgName = opts.package ?? path30.basename(path30.dirname(changelogPath));
9103
+ const pkgName = opts.package ?? path31.basename(path31.dirname(changelogPath));
8913
9104
  const scope = opts.scope ?? "team";
8914
- const teamDir = path30.join(paths.memoriesDir, scope);
8915
- await mkdir13(teamDir, { recursive: true });
9105
+ const teamDir = path31.join(paths.memoriesDir, scope);
9106
+ await mkdir14(teamDir, { recursive: true });
8916
9107
  let saved = 0;
8917
9108
  for (const entry of entries) {
8918
9109
  const lines = [];
@@ -8934,7 +9125,7 @@ function registerMemoryImportChangelog(memory2) {
8934
9125
  lines.push("");
8935
9126
  }
8936
9127
  lines.push(
8937
- `**Source:** \`${path30.relative(root, changelogPath)}\`
9128
+ `**Source:** \`${path31.relative(root, changelogPath)}\`
8938
9129
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
8939
9130
  );
8940
9131
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -8949,11 +9140,11 @@ function registerMemoryImportChangelog(memory2) {
8949
9140
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
8950
9141
  `v${entry.version}`
8951
9142
  ],
8952
- paths: [path30.relative(root, changelogPath)],
9143
+ paths: [path31.relative(root, changelogPath)],
8953
9144
  topic: `changelog-${pkgName}-${entry.version}`
8954
9145
  });
8955
9146
  await writeFile23(
8956
- path30.join(teamDir, `${fm.id}.md`),
9147
+ path31.join(teamDir, `${fm.id}.md`),
8957
9148
  serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
8958
9149
  "utf8"
8959
9150
  );
@@ -8978,15 +9169,15 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
8978
9169
  // src/commands/memory-digest.ts
8979
9170
  import { existsSync as existsSync51 } from "fs";
8980
9171
  import { writeFile as writeFile24 } from "fs/promises";
8981
- import path31 from "path";
9172
+ import path33 from "path";
8982
9173
  import "commander";
8983
9174
  import {
8984
9175
  deriveConfidence as deriveConfidence12,
8985
- findProjectRoot as findProjectRoot30,
9176
+ findProjectRoot as findProjectRoot31,
8986
9177
  getUsage as getUsage17,
8987
9178
  loadMemoriesFromDir as loadMemoriesFromDir26,
8988
9179
  loadUsageIndex as loadUsageIndex21,
8989
- resolveHaivePaths as resolveHaivePaths27
9180
+ resolveHaivePaths as resolveHaivePaths28
8990
9181
  } from "@hiveai/core";
8991
9182
  var CONFIDENCE_EMOJI = {
8992
9183
  unverified: "\u2B1C",
@@ -8999,8 +9190,8 @@ function registerMemoryDigest(program2) {
8999
9190
  program2.command("digest").description(
9000
9191
  "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"
9001
9192
  ).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) => {
9002
- const root = findProjectRoot30(opts.dir);
9003
- const paths = resolveHaivePaths27(root);
9193
+ const root = findProjectRoot31(opts.dir);
9194
+ const paths = resolveHaivePaths28(root);
9004
9195
  if (!existsSync51(paths.memoriesDir)) {
9005
9196
  ui.error("No .ai/memories found. Run `haive init` first.");
9006
9197
  process.exitCode = 1;
@@ -9073,7 +9264,7 @@ function registerMemoryDigest(program2) {
9073
9264
  );
9074
9265
  const digest = lines.join("\n");
9075
9266
  if (opts.out) {
9076
- const outPath = path31.resolve(process.cwd(), opts.out);
9267
+ const outPath = path33.resolve(process.cwd(), opts.out);
9077
9268
  await writeFile24(outPath, digest, "utf8");
9078
9269
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
9079
9270
  } else {
@@ -9083,20 +9274,20 @@ function registerMemoryDigest(program2) {
9083
9274
  }
9084
9275
 
9085
9276
  // src/commands/session-end.ts
9086
- import { writeFile as writeFile25, mkdir as mkdir14, readFile as readFile14, rm as rm2 } from "fs/promises";
9277
+ import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile14, rm as rm2 } from "fs/promises";
9087
9278
  import { existsSync as existsSync53 } from "fs";
9088
- import path33 from "path";
9279
+ import path34 from "path";
9089
9280
  import "commander";
9090
9281
  import {
9091
9282
  buildFrontmatter as buildFrontmatter10,
9092
- findProjectRoot as findProjectRoot31,
9283
+ findProjectRoot as findProjectRoot32,
9093
9284
  loadMemoriesFromDir as loadMemoriesFromDir27,
9094
9285
  memoryFilePath as memoryFilePath9,
9095
- resolveHaivePaths as resolveHaivePaths28,
9286
+ resolveHaivePaths as resolveHaivePaths29,
9096
9287
  serializeMemory as serializeMemory21
9097
9288
  } from "@hiveai/core";
9098
9289
  async function buildAutoRecap(paths) {
9099
- const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
9290
+ const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
9100
9291
  if (!existsSync53(obsFile)) return null;
9101
9292
  const raw = await readFile14(obsFile, "utf8").catch(() => "");
9102
9293
  if (!raw.trim()) return null;
@@ -9176,8 +9367,8 @@ function registerSessionEnd(session2) {
9176
9367
  --next "Add integration tests for webhook signature validation"
9177
9368
  `
9178
9369
  ).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) => {
9179
- const root = findProjectRoot31(opts.dir);
9180
- const paths = resolveHaivePaths28(root);
9370
+ const root = findProjectRoot32(opts.dir);
9371
+ const paths = resolveHaivePaths29(root);
9181
9372
  if (!existsSync53(paths.haiveDir)) {
9182
9373
  if (opts.auto || opts.quiet) return;
9183
9374
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
@@ -9210,14 +9401,14 @@ function registerSessionEnd(session2) {
9210
9401
  });
9211
9402
  const topic = recapTopic2(scope, opts.module);
9212
9403
  const filesTouched = parseCsv5(resolvedFiles);
9213
- const missingPaths = filesTouched.filter((p) => !existsSync53(path33.resolve(root, p)));
9404
+ const missingPaths = filesTouched.filter((p) => !existsSync53(path34.resolve(root, p)));
9214
9405
  if (missingPaths.length > 0 && !opts.quiet) {
9215
9406
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
9216
9407
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
9217
9408
  }
9218
9409
  const cleanupObservations = async () => {
9219
9410
  if (!opts.auto) return;
9220
- const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
9411
+ const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
9221
9412
  if (existsSync53(obsFile)) await rm2(obsFile).catch(() => {
9222
9413
  });
9223
9414
  };
@@ -9231,6 +9422,7 @@ function registerSessionEnd(session2) {
9231
9422
  const revisionCount = (fm.revision_count ?? 0) + 1;
9232
9423
  const newFrontmatter = {
9233
9424
  ...fm,
9425
+ verified_at: (/* @__PURE__ */ new Date()).toISOString(),
9234
9426
  revision_count: revisionCount,
9235
9427
  anchor: {
9236
9428
  ...fm.anchor,
@@ -9241,7 +9433,7 @@ function registerSessionEnd(session2) {
9241
9433
  await cleanupObservations();
9242
9434
  if (!opts.quiet) {
9243
9435
  ui.success(`Session recap updated (revision #${revisionCount})`);
9244
- ui.info(`id=${fm.id} file=${path33.relative(root, topicMatch.filePath)}`);
9436
+ ui.info(`id=${fm.id} file=${path34.relative(root, topicMatch.filePath)}`);
9245
9437
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
9246
9438
  }
9247
9439
  return;
@@ -9258,12 +9450,12 @@ function registerSessionEnd(session2) {
9258
9450
  status: "validated"
9259
9451
  });
9260
9452
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9261
- await mkdir14(path33.dirname(file), { recursive: true });
9453
+ await mkdir15(path34.dirname(file), { recursive: true });
9262
9454
  await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
9263
9455
  await cleanupObservations();
9264
9456
  if (!opts.quiet) {
9265
9457
  ui.success(`Session recap created`);
9266
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path33.relative(root, file)}`);
9458
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path34.relative(root, file)}`);
9267
9459
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
9268
9460
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
9269
9461
  }
@@ -9277,13 +9469,13 @@ function parseCsv5(value) {
9277
9469
  // src/commands/snapshot.ts
9278
9470
  import { existsSync as existsSync54 } from "fs";
9279
9471
  import { readdir as readdir4 } from "fs/promises";
9280
- import path34 from "path";
9472
+ import path35 from "path";
9281
9473
  import "commander";
9282
9474
  import {
9283
9475
  diffContract,
9284
- findProjectRoot as findProjectRoot32,
9476
+ findProjectRoot as findProjectRoot33,
9285
9477
  loadConfig as loadConfig5,
9286
- resolveHaivePaths as resolveHaivePaths29,
9478
+ resolveHaivePaths as resolveHaivePaths30,
9287
9479
  snapshotContract
9288
9480
  } from "@hiveai/core";
9289
9481
  function registerSnapshot(program2) {
@@ -9308,15 +9500,15 @@ function registerSnapshot(program2) {
9308
9500
  "--format <format>",
9309
9501
  "contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
9310
9502
  ).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) => {
9311
- const root = findProjectRoot32(opts.dir);
9312
- const paths = resolveHaivePaths29(root);
9503
+ const root = findProjectRoot33(opts.dir);
9504
+ const paths = resolveHaivePaths30(root);
9313
9505
  if (!existsSync54(paths.haiveDir)) {
9314
9506
  ui.error("No .ai/ found. Run `haive init` first.");
9315
9507
  process.exitCode = 1;
9316
9508
  return;
9317
9509
  }
9318
9510
  if (opts.list) {
9319
- const contractsDir = path34.join(paths.haiveDir, "contracts");
9511
+ const contractsDir = path35.join(paths.haiveDir, "contracts");
9320
9512
  if (!existsSync54(contractsDir)) {
9321
9513
  console.log(ui.dim("No contract snapshots found."));
9322
9514
  return;
@@ -9372,7 +9564,7 @@ function registerSnapshot(program2) {
9372
9564
  return;
9373
9565
  }
9374
9566
  const contractPath = opts.contract;
9375
- const name = opts.name ?? path34.basename(contractPath, path34.extname(contractPath));
9567
+ const name = opts.name ?? path35.basename(contractPath, path35.extname(contractPath));
9376
9568
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
9377
9569
  const contract = { name, path: contractPath, format };
9378
9570
  try {
@@ -9427,8 +9619,8 @@ async function runDiff(root, haiveDir, contract) {
9427
9619
  }
9428
9620
  }
9429
9621
  function detectFormat(filePath) {
9430
- const ext = path34.extname(filePath).toLowerCase();
9431
- const base = path34.basename(filePath).toLowerCase();
9622
+ const ext = path35.extname(filePath).toLowerCase();
9623
+ const base = path35.basename(filePath).toLowerCase();
9432
9624
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
9433
9625
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
9434
9626
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -9442,15 +9634,15 @@ function detectFormat(filePath) {
9442
9634
 
9443
9635
  // src/commands/hub.ts
9444
9636
  import { existsSync as existsSync55 } from "fs";
9445
- import { mkdir as mkdir15, readFile as readFile15, writeFile as writeFile26, copyFile } from "fs/promises";
9446
- import path35 from "path";
9447
- import { spawnSync as spawnSync3 } from "child_process";
9637
+ import { mkdir as mkdir16, readFile as readFile15, writeFile as writeFile26, copyFile } from "fs/promises";
9638
+ import path36 from "path";
9639
+ import { spawnSync as spawnSync4 } from "child_process";
9448
9640
  import "commander";
9449
9641
  import {
9450
- findProjectRoot as findProjectRoot33,
9642
+ findProjectRoot as findProjectRoot34,
9451
9643
  loadConfig as loadConfig6,
9452
9644
  loadMemoriesFromDir as loadMemoriesFromDir28,
9453
- resolveHaivePaths as resolveHaivePaths30,
9645
+ resolveHaivePaths as resolveHaivePaths31,
9454
9646
  saveConfig as saveConfig2,
9455
9647
  serializeMemory as serializeMemory23
9456
9648
  } from "@hiveai/core";
@@ -9462,21 +9654,21 @@ function registerHub(program2) {
9462
9654
  hub.command("init <hubPath>").description(
9463
9655
  "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"
9464
9656
  ).action(async (hubPath) => {
9465
- const absPath = path35.resolve(hubPath);
9466
- await mkdir15(absPath, { recursive: true });
9467
- const gitCheck = spawnSync3("git", ["rev-parse", "--git-dir"], { cwd: absPath });
9657
+ const absPath = path36.resolve(hubPath);
9658
+ await mkdir16(absPath, { recursive: true });
9659
+ const gitCheck = spawnSync4("git", ["rev-parse", "--git-dir"], { cwd: absPath });
9468
9660
  if (gitCheck.status !== 0) {
9469
- const init = spawnSync3("git", ["init"], { cwd: absPath, encoding: "utf8" });
9661
+ const init = spawnSync4("git", ["init"], { cwd: absPath, encoding: "utf8" });
9470
9662
  if (init.status !== 0) {
9471
9663
  ui.error(`git init failed: ${init.stderr}`);
9472
9664
  process.exitCode = 1;
9473
9665
  return;
9474
9666
  }
9475
9667
  }
9476
- const sharedDir = path35.join(absPath, ".ai", "memories", "shared");
9477
- await mkdir15(sharedDir, { recursive: true });
9668
+ const sharedDir = path36.join(absPath, ".ai", "memories", "shared");
9669
+ await mkdir16(sharedDir, { recursive: true });
9478
9670
  await writeFile26(
9479
- path35.join(absPath, ".ai", "README.md"),
9671
+ path36.join(absPath, ".ai", "README.md"),
9480
9672
  `# hAIve Team Knowledge Hub
9481
9673
 
9482
9674
  This repo is a shared knowledge hub for hAIve.
@@ -9498,12 +9690,12 @@ haive hub pull # import into a project
9498
9690
  "utf8"
9499
9691
  );
9500
9692
  await writeFile26(
9501
- path35.join(absPath, ".gitignore"),
9693
+ path36.join(absPath, ".gitignore"),
9502
9694
  ".ai/.cache/\n.ai/memories/personal/\n",
9503
9695
  "utf8"
9504
9696
  );
9505
- spawnSync3("git", ["add", "."], { cwd: absPath });
9506
- spawnSync3("git", ["commit", "-m", "chore: initialize hAIve team-knowledge hub"], {
9697
+ spawnSync4("git", ["add", "."], { cwd: absPath });
9698
+ spawnSync4("git", ["commit", "-m", "chore: initialize hAIve team-knowledge hub"], {
9507
9699
  cwd: absPath,
9508
9700
  encoding: "utf8"
9509
9701
  });
@@ -9513,7 +9705,7 @@ haive hub pull # import into a project
9513
9705
  `
9514
9706
  Next steps:
9515
9707
  1. Add hubPath to your project's .ai/haive.config.json:
9516
- { "hubPath": "${path35.relative(process.cwd(), absPath)}" }
9708
+ { "hubPath": "${path36.relative(process.cwd(), absPath)}" }
9517
9709
  2. Run \`haive hub push\` to publish your shared memories
9518
9710
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
9519
9711
  `
@@ -9532,8 +9724,8 @@ Next steps:
9532
9724
  haive hub push --commit --message "feat: add payment API contract memories"
9533
9725
  `
9534
9726
  ).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) => {
9535
- const root = findProjectRoot33(opts.dir);
9536
- const paths = resolveHaivePaths30(root);
9727
+ const root = findProjectRoot34(opts.dir);
9728
+ const paths = resolveHaivePaths31(root);
9537
9729
  const config = await loadConfig6(paths);
9538
9730
  if (!config.hubPath) {
9539
9731
  ui.error(
@@ -9542,15 +9734,15 @@ Next steps:
9542
9734
  process.exitCode = 1;
9543
9735
  return;
9544
9736
  }
9545
- const hubRoot = path35.resolve(root, config.hubPath);
9737
+ const hubRoot = path36.resolve(root, config.hubPath);
9546
9738
  if (!existsSync55(hubRoot)) {
9547
9739
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
9548
9740
  process.exitCode = 1;
9549
9741
  return;
9550
9742
  }
9551
- const projectName = path35.basename(root);
9552
- const destDir = path35.join(hubRoot, ".ai", "memories", "shared", projectName);
9553
- await mkdir15(destDir, { recursive: true });
9743
+ const projectName = path36.basename(root);
9744
+ const destDir = path36.join(hubRoot, ".ai", "memories", "shared", projectName);
9745
+ await mkdir16(destDir, { recursive: true });
9554
9746
  const all = await loadMemoriesFromDir28(paths.memoriesDir);
9555
9747
  const shared = all.filter(
9556
9748
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
@@ -9568,7 +9760,7 @@ Next steps:
9568
9760
  for (const { memory: memory2 } of shared) {
9569
9761
  const fm = memory2.frontmatter;
9570
9762
  const fileName = `${fm.id}.md`;
9571
- const destPath = path35.join(destDir, fileName);
9763
+ const destPath = path36.join(destDir, fileName);
9572
9764
  await writeFile26(destPath, serializeMemory23(memory2), "utf8");
9573
9765
  pushed++;
9574
9766
  }
@@ -9576,10 +9768,10 @@ Next steps:
9576
9768
  console.log(ui.dim(` Location: ${destDir}`));
9577
9769
  if (opts.commit) {
9578
9770
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
9579
- spawnSync3("git", ["add", path35.join(".ai", "memories", "shared", projectName)], {
9771
+ spawnSync4("git", ["add", path36.join(".ai", "memories", "shared", projectName)], {
9580
9772
  cwd: hubRoot
9581
9773
  });
9582
- const commit = spawnSync3("git", ["commit", "-m", message], {
9774
+ const commit = spawnSync4("git", ["commit", "-m", message], {
9583
9775
  cwd: hubRoot,
9584
9776
  encoding: "utf8"
9585
9777
  });
@@ -9601,8 +9793,8 @@ Next steps:
9601
9793
  hub.command("pull").description(
9602
9794
  "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"
9603
9795
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
9604
- const root = findProjectRoot33(opts.dir);
9605
- const paths = resolveHaivePaths30(root);
9796
+ const root = findProjectRoot34(opts.dir);
9797
+ const paths = resolveHaivePaths31(root);
9606
9798
  const config = await loadConfig6(paths);
9607
9799
  if (!config.hubPath) {
9608
9800
  ui.error(
@@ -9611,13 +9803,13 @@ Next steps:
9611
9803
  process.exitCode = 1;
9612
9804
  return;
9613
9805
  }
9614
- const hubRoot = path35.resolve(root, config.hubPath);
9615
- const hubSharedDir = path35.join(hubRoot, ".ai", "memories", "shared");
9806
+ const hubRoot = path36.resolve(root, config.hubPath);
9807
+ const hubSharedDir = path36.join(hubRoot, ".ai", "memories", "shared");
9616
9808
  if (!existsSync55(hubSharedDir)) {
9617
9809
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
9618
9810
  return;
9619
9811
  }
9620
- const projectName = path35.basename(root);
9812
+ const projectName = path36.basename(root);
9621
9813
  const { readdir: readdir6 } = await import("fs/promises");
9622
9814
  const projectDirs = (await readdir6(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
9623
9815
  if (projectDirs.length === 0) {
@@ -9627,16 +9819,16 @@ Next steps:
9627
9819
  let totalImported = 0;
9628
9820
  let totalUpdated = 0;
9629
9821
  for (const sourceName of projectDirs) {
9630
- const sourceDir = path35.join(hubSharedDir, sourceName);
9631
- const destDir = path35.join(paths.memoriesDir, "shared", sourceName);
9632
- await mkdir15(destDir, { recursive: true });
9822
+ const sourceDir = path36.join(hubSharedDir, sourceName);
9823
+ const destDir = path36.join(paths.memoriesDir, "shared", sourceName);
9824
+ await mkdir16(destDir, { recursive: true });
9633
9825
  const sourceFiles = (await readdir6(sourceDir)).filter((f) => f.endsWith(".md"));
9634
9826
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
9635
9827
  const existingInDest = await loadDir(destDir);
9636
9828
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
9637
9829
  for (const file of sourceFiles) {
9638
- const srcPath = path35.join(sourceDir, file);
9639
- const destPath = path35.join(destDir, file);
9830
+ const srcPath = path36.join(sourceDir, file);
9831
+ const destPath = path36.join(destDir, file);
9640
9832
  const fileContent = await readFile15(srcPath, "utf8");
9641
9833
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
9642
9834
  if (!alreadyTagged) {
@@ -9660,21 +9852,21 @@ Next steps:
9660
9852
  );
9661
9853
  });
9662
9854
  hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
9663
- const root = findProjectRoot33(opts.dir);
9664
- const paths = resolveHaivePaths30(root);
9855
+ const root = findProjectRoot34(opts.dir);
9856
+ const paths = resolveHaivePaths31(root);
9665
9857
  const config = await loadConfig6(paths);
9666
9858
  console.log(ui.bold("Hub status"));
9667
9859
  console.log(
9668
9860
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
9669
9861
  );
9670
- const sharedDir = path35.join(paths.memoriesDir, "shared");
9862
+ const sharedDir = path36.join(paths.memoriesDir, "shared");
9671
9863
  if (existsSync55(sharedDir)) {
9672
9864
  const { readdir: readdir6 } = await import("fs/promises");
9673
9865
  const sources = (await readdir6(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
9674
9866
  console.log(`
9675
9867
  Imported from ${sources.length} source(s):`);
9676
9868
  for (const src of sources) {
9677
- const files = (await readdir6(path35.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
9869
+ const files = (await readdir6(path36.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
9678
9870
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
9679
9871
  }
9680
9872
  } else {
@@ -9698,16 +9890,16 @@ Next steps:
9698
9890
  // src/commands/stats.ts
9699
9891
  import "commander";
9700
9892
  import { existsSync as existsSync56 } from "fs";
9701
- import { mkdir as mkdir16, writeFile as writeFile27 } from "fs/promises";
9702
- import path36 from "path";
9893
+ import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
9894
+ import path37 from "path";
9703
9895
  import {
9704
9896
  aggregateUsage,
9705
- findProjectRoot as findProjectRoot34,
9897
+ findProjectRoot as findProjectRoot35,
9706
9898
  loadMemoriesFromDir as loadMemoriesFromDir29,
9707
9899
  loadUsageIndex as loadUsageIndex23,
9708
9900
  parseSince,
9709
9901
  readUsageEvents as readUsageEvents2,
9710
- resolveHaivePaths as resolveHaivePaths31,
9902
+ resolveHaivePaths as resolveHaivePaths32,
9711
9903
  usageLogSize
9712
9904
  } from "@hiveai/core";
9713
9905
  function registerStats(program2) {
@@ -9716,8 +9908,8 @@ function registerStats(program2) {
9716
9908
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
9717
9909
  void 0
9718
9910
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
9719
- const root = findProjectRoot34(opts.dir);
9720
- const paths = resolveHaivePaths31(root);
9911
+ const root = findProjectRoot35(opts.dir);
9912
+ const paths = resolveHaivePaths32(root);
9721
9913
  if (opts.exportReport) {
9722
9914
  await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
9723
9915
  return;
@@ -9771,7 +9963,7 @@ function registerStats(program2) {
9771
9963
  });
9772
9964
  }
9773
9965
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
9774
- const outAbs = path36.isAbsolute(outRelative) ? path36.resolve(outRelative) : path36.resolve(root, outRelative);
9966
+ const outAbs = path37.isAbsolute(outRelative) ? path37.resolve(outRelative) : path37.resolve(root, outRelative);
9775
9967
  const size = await usageLogSize(paths);
9776
9968
  let events = await readUsageEvents2(paths);
9777
9969
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
@@ -9806,7 +9998,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
9806
9998
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
9807
9999
  events = [];
9808
10000
  }
9809
- await mkdir16(path36.dirname(outAbs), { recursive: true });
10001
+ await mkdir17(path37.dirname(outAbs), { recursive: true });
9810
10002
  const payload = {
9811
10003
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
9812
10004
  project_root: root,
@@ -9870,13 +10062,13 @@ import { performance } from "perf_hooks";
9870
10062
  import "commander";
9871
10063
  import {
9872
10064
  estimateTokens as estimateTokens3,
9873
- findProjectRoot as findProjectRoot35,
9874
- resolveHaivePaths as resolveHaivePaths32
10065
+ findProjectRoot as findProjectRoot36,
10066
+ resolveHaivePaths as resolveHaivePaths33
9875
10067
  } from "@hiveai/core";
9876
10068
  function registerBench(program2) {
9877
10069
  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) => {
9878
- const root = findProjectRoot35(opts.dir);
9879
- const paths = resolveHaivePaths32(root);
10070
+ const root = findProjectRoot36(opts.dir);
10071
+ const paths = resolveHaivePaths33(root);
9880
10072
  const ctx = { paths };
9881
10073
  const task = opts.task ?? "audit dependencies for security risks";
9882
10074
  const scenarios = [
@@ -9997,9 +10189,9 @@ function summarize(name, t0, payload, notes) {
9997
10189
  // src/commands/benchmark.ts
9998
10190
  import { existsSync as existsSync57 } from "fs";
9999
10191
  import { readdir as readdir5, readFile as readFile16, writeFile as writeFile28 } from "fs/promises";
10000
- import path37 from "path";
10192
+ import path38 from "path";
10001
10193
  import "commander";
10002
- import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot36 } from "@hiveai/core";
10194
+ import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot37 } from "@hiveai/core";
10003
10195
  function registerBenchmark(program2) {
10004
10196
  const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
10005
10197
  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) => {
@@ -10012,9 +10204,9 @@ function registerBenchmark(program2) {
10012
10204
  }
10013
10205
  const markdown = renderMarkdown(root, summary, rows);
10014
10206
  if (opts.out) {
10015
- const outFile = path37.isAbsolute(opts.out) ? opts.out : path37.join(root, opts.out);
10207
+ const outFile = path38.isAbsolute(opts.out) ? opts.out : path38.join(root, opts.out);
10016
10208
  await writeFile28(outFile, markdown, "utf8");
10017
- ui.success(`wrote ${path37.relative(process.cwd(), outFile)}`);
10209
+ ui.success(`wrote ${path38.relative(process.cwd(), outFile)}`);
10018
10210
  return;
10019
10211
  }
10020
10212
  console.log(markdown);
@@ -10038,9 +10230,9 @@ function registerBenchmark(program2) {
10038
10230
  }
10039
10231
  function resolveBenchmarkRoot(dir) {
10040
10232
  const candidate = dir ?? "benchmarks/agent-benchmark";
10041
- if (path37.isAbsolute(candidate)) return candidate;
10042
- const projectRoot = findProjectRoot36(process.cwd());
10043
- return path37.join(projectRoot, candidate);
10233
+ if (path38.isAbsolute(candidate)) return candidate;
10234
+ const projectRoot = findProjectRoot37(process.cwd());
10235
+ return path38.join(projectRoot, candidate);
10044
10236
  }
10045
10237
  async function collectRows(root) {
10046
10238
  if (!existsSync57(root)) throw new Error(`Benchmark directory not found: ${root}`);
@@ -10048,8 +10240,8 @@ async function collectRows(root) {
10048
10240
  const rows = [];
10049
10241
  for (const entry of entries) {
10050
10242
  if (!entry.isDirectory()) continue;
10051
- const fixtureDir = path37.join(root, entry.name);
10052
- const reportFile = path37.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
10243
+ const fixtureDir = path38.join(root, entry.name);
10244
+ const reportFile = path38.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
10053
10245
  if (!existsSync57(reportFile)) continue;
10054
10246
  const report = await readFile16(reportFile, "utf8");
10055
10247
  rows.push(parseAgentReport(entry.name, report));
@@ -10140,19 +10332,19 @@ function escapeRegExp(value) {
10140
10332
  }
10141
10333
 
10142
10334
  // src/commands/memory-suggest.ts
10143
- import { mkdir as mkdir17, writeFile as writeFile29 } from "fs/promises";
10335
+ import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
10144
10336
  import { existsSync as existsSync58 } from "fs";
10145
- import path38 from "path";
10337
+ import path39 from "path";
10146
10338
  import "commander";
10147
10339
  import {
10148
10340
  aggregateUsage as aggregateUsage2,
10149
10341
  buildFrontmatter as buildFrontmatter11,
10150
- findProjectRoot as findProjectRoot37,
10342
+ findProjectRoot as findProjectRoot38,
10151
10343
  loadMemoriesFromDir as loadMemoriesFromDir30,
10152
10344
  memoryFilePath as memoryFilePath10,
10153
10345
  parseSince as parseSince2,
10154
10346
  readUsageEvents as readUsageEvents3,
10155
- resolveHaivePaths as resolveHaivePaths33,
10347
+ resolveHaivePaths as resolveHaivePaths34,
10156
10348
  serializeMemory as serializeMemory24
10157
10349
  } from "@hiveai/core";
10158
10350
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
@@ -10165,8 +10357,8 @@ function registerMemorySuggest(memory2) {
10165
10357
  memory2.command("suggest").description(
10166
10358
  "Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to draft the top-N suggestions as draft memories. They land\n in personal scope by default with status=draft, ready for you to edit and promote."
10167
10359
  ).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of drafted memories (personal | team)", "personal").option("--auto-save", "draft top-N suggestions as draft memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
10168
- const root = findProjectRoot37(opts.dir);
10169
- const paths = resolveHaivePaths33(root);
10360
+ const root = findProjectRoot38(opts.dir);
10361
+ const paths = resolveHaivePaths34(root);
10170
10362
  const events = await readUsageEvents3(paths);
10171
10363
  if (events.length === 0) {
10172
10364
  if (opts.json) {
@@ -10235,13 +10427,13 @@ function registerMemorySuggest(memory2) {
10235
10427
  fm.status = "draft";
10236
10428
  const body = renderTemplate(s);
10237
10429
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
10238
- await mkdir17(path38.dirname(file), { recursive: true });
10430
+ await mkdir18(path39.dirname(file), { recursive: true });
10239
10431
  if (existsSync58(file)) {
10240
- skipped.push({ query: s.query, reason: `file already exists at ${path38.relative(root, file)}` });
10432
+ skipped.push({ query: s.query, reason: `file already exists at ${path39.relative(root, file)}` });
10241
10433
  continue;
10242
10434
  }
10243
10435
  await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
10244
- created.push({ id: fm.id, file: path38.relative(root, file), query: s.query });
10436
+ created.push({ id: fm.id, file: path39.relative(root, file), query: s.query });
10245
10437
  }
10246
10438
  if (opts.json) {
10247
10439
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -10336,14 +10528,14 @@ function truncate2(text, max) {
10336
10528
  // src/commands/memory-archive.ts
10337
10529
  import { existsSync as existsSync59 } from "fs";
10338
10530
  import { writeFile as writeFile30 } from "fs/promises";
10339
- import path39 from "path";
10531
+ import path40 from "path";
10340
10532
  import "commander";
10341
10533
  import {
10342
- findProjectRoot as findProjectRoot38,
10534
+ findProjectRoot as findProjectRoot39,
10343
10535
  getUsage as getUsage18,
10344
10536
  loadMemoriesFromDir as loadMemoriesFromDir31,
10345
10537
  loadUsageIndex as loadUsageIndex24,
10346
- resolveHaivePaths as resolveHaivePaths34,
10538
+ resolveHaivePaths as resolveHaivePaths35,
10347
10539
  serializeMemory as serializeMemory25
10348
10540
  } from "@hiveai/core";
10349
10541
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
@@ -10351,8 +10543,8 @@ function registerMemoryArchive(memory2) {
10351
10543
  memory2.command("archive").description(
10352
10544
  "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."
10353
10545
  ).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) => {
10354
- const root = findProjectRoot38(opts.dir);
10355
- const paths = resolveHaivePaths34(root);
10546
+ const root = findProjectRoot39(opts.dir);
10547
+ const paths = resolveHaivePaths35(root);
10356
10548
  if (!existsSync59(paths.memoriesDir)) {
10357
10549
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10358
10550
  process.exitCode = 1;
@@ -10374,7 +10566,7 @@ function registerMemoryArchive(memory2) {
10374
10566
  if (typeFilter && fm.type !== typeFilter) continue;
10375
10567
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
10376
10568
  const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
10377
- const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(path39.join(paths.root, p)));
10569
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(path40.join(paths.root, p)));
10378
10570
  const isAnchorless = !hasAnyAnchor;
10379
10571
  if (!isAnchorless && !allPathsGone) continue;
10380
10572
  const u = getUsage18(usage, fm.id);
@@ -10450,27 +10642,27 @@ function parseDays(input) {
10450
10642
  // src/commands/doctor.ts
10451
10643
  import { existsSync as existsSync60 } from "fs";
10452
10644
  import { stat } from "fs/promises";
10453
- import path40 from "path";
10645
+ import path41 from "path";
10454
10646
  import { execSync as execSync3 } from "child_process";
10455
10647
  import "commander";
10456
10648
  import {
10457
10649
  codeMapPath as codeMapPath2,
10458
- findProjectRoot as findProjectRoot39,
10650
+ findProjectRoot as findProjectRoot40,
10459
10651
  getUsage as getUsage19,
10460
10652
  loadCodeMap as loadCodeMap5,
10461
10653
  loadConfig as loadConfig7,
10462
10654
  loadMemoriesFromDir as loadMemoriesFromDir32,
10463
10655
  loadUsageIndex as loadUsageIndex25,
10464
10656
  readUsageEvents as readUsageEvents4,
10465
- resolveHaivePaths as resolveHaivePaths35
10657
+ resolveHaivePaths as resolveHaivePaths36
10466
10658
  } from "@hiveai/core";
10467
10659
  var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
10468
10660
  function registerDoctor(program2) {
10469
10661
  program2.command("doctor").description(
10470
10662
  "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 suggest commands you can copy-paste."
10471
10663
  ).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
10472
- const root = findProjectRoot39(opts.dir);
10473
- const paths = resolveHaivePaths35(root);
10664
+ const root = findProjectRoot40(opts.dir);
10665
+ const paths = resolveHaivePaths36(root);
10474
10666
  const findings = [];
10475
10667
  if (!existsSync60(paths.haiveDir)) {
10476
10668
  findings.push({
@@ -10612,7 +10804,7 @@ function registerDoctor(program2) {
10612
10804
  }
10613
10805
  const config = await loadConfig7(paths);
10614
10806
  if (config.enforcement?.requireBriefingFirst) {
10615
- const claudeSettings = path40.join(root, ".claude", "settings.local.json");
10807
+ const claudeSettings = path41.join(root, ".claude", "settings.local.json");
10616
10808
  let hasClaudeEnforcement = false;
10617
10809
  if (existsSync60(claudeSettings)) {
10618
10810
  try {
@@ -10646,7 +10838,7 @@ function registerDoctor(program2) {
10646
10838
  timeout: 3e3,
10647
10839
  stdio: ["ignore", "pipe", "ignore"]
10648
10840
  }).trim();
10649
- const cliVersion = "0.9.11";
10841
+ const cliVersion = "0.9.13";
10650
10842
  if (legacyRaw && legacyRaw !== cliVersion) {
10651
10843
  findings.push({
10652
10844
  severity: "warn",
@@ -10698,19 +10890,19 @@ function isSearchTool(name) {
10698
10890
  import { existsSync as existsSync61 } from "fs";
10699
10891
  import "commander";
10700
10892
  import {
10701
- findProjectRoot as findProjectRoot40,
10893
+ findProjectRoot as findProjectRoot41,
10702
10894
  loadMemoriesFromDir as loadMemoriesFromDir33,
10703
10895
  parseSince as parseSince3,
10704
10896
  readUsageEvents as readUsageEvents5,
10705
- resolveHaivePaths as resolveHaivePaths36
10897
+ resolveHaivePaths as resolveHaivePaths37
10706
10898
  } from "@hiveai/core";
10707
10899
  var MS_PER_MINUTE = 6e4;
10708
10900
  function registerPlayback(program2) {
10709
10901
  program2.command("playback").description(
10710
10902
  "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?'"
10711
10903
  ).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) => {
10712
- const root = findProjectRoot40(opts.dir);
10713
- const paths = resolveHaivePaths36(root);
10904
+ const root = findProjectRoot41(opts.dir);
10905
+ const paths = resolveHaivePaths37(root);
10714
10906
  const events = await readUsageEvents5(paths);
10715
10907
  if (events.length === 0) {
10716
10908
  if (opts.json) {
@@ -10815,8 +11007,8 @@ function truncate3(text, max) {
10815
11007
  import { spawn as spawn4 } from "child_process";
10816
11008
  import "commander";
10817
11009
  import {
10818
- findProjectRoot as findProjectRoot41,
10819
- resolveHaivePaths as resolveHaivePaths37
11010
+ findProjectRoot as findProjectRoot42,
11011
+ resolveHaivePaths as resolveHaivePaths38
10820
11012
  } from "@hiveai/core";
10821
11013
  function registerPrecommit(program2) {
10822
11014
  program2.command("precommit").description(
@@ -10826,8 +11018,8 @@ function registerPrecommit(program2) {
10826
11018
  "'any' | 'high-confidence' (default) | 'never' (report only)",
10827
11019
  "high-confidence"
10828
11020
  ).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) => {
10829
- const root = findProjectRoot41(opts.dir);
10830
- const paths = resolveHaivePaths37(root);
11021
+ const root = findProjectRoot42(opts.dir);
11022
+ const paths = resolveHaivePaths38(root);
10831
11023
  const ctx = { paths };
10832
11024
  let diff = "";
10833
11025
  let touchedPaths = opts.paths ?? [];
@@ -10858,7 +11050,7 @@ function registerPrecommit(program2) {
10858
11050
  console.log(ui.bold(`hAIve precommit \u2014 ${touchedPaths.length} file(s)`));
10859
11051
  console.log(
10860
11052
  ui.dim(
10861
- ` anti-patterns: ${result.summary.anti_patterns} relevant memories: ${result.summary.relevant_memories} stale anchors: ${result.summary.stale_anchors}`
11053
+ ` anti-patterns: ${result.summary.anti_patterns} blocking: ${result.summary.blocking_warnings ?? result.summary.anti_patterns} relevant memories: ${result.summary.relevant_memories} stale anchors: ${result.summary.stale_anchors}`
10862
11054
  )
10863
11055
  );
10864
11056
  console.log();
@@ -10922,9 +11114,9 @@ function runCommand3(cmd, args, cwd) {
10922
11114
  import { existsSync as existsSync63 } from "fs";
10923
11115
  import "commander";
10924
11116
  import {
10925
- findProjectRoot as findProjectRoot42,
11117
+ findProjectRoot as findProjectRoot43,
10926
11118
  loadMemoriesFromDir as loadMemoriesFromDir34,
10927
- resolveHaivePaths as resolveHaivePaths38
11119
+ resolveHaivePaths as resolveHaivePaths39
10928
11120
  } from "@hiveai/core";
10929
11121
  var TYPE_RANK = {
10930
11122
  decision: 0,
@@ -10938,8 +11130,8 @@ function registerWelcome(program2) {
10938
11130
  program2.command("welcome").description(
10939
11131
  "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"
10940
11132
  ).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
10941
- const root = findProjectRoot42(opts.dir);
10942
- const paths = resolveHaivePaths38(root);
11133
+ const root = findProjectRoot43(opts.dir);
11134
+ const paths = resolveHaivePaths39(root);
10943
11135
  if (!existsSync63(paths.memoriesDir)) {
10944
11136
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
10945
11137
  process.exitCode = 1;
@@ -10984,12 +11176,12 @@ function registerWelcome(program2) {
10984
11176
  import { existsSync as existsSync64 } from "fs";
10985
11177
  import "commander";
10986
11178
  import {
10987
- findProjectRoot as findProjectRoot43,
11179
+ findProjectRoot as findProjectRoot44,
10988
11180
  loadMemoriesFromDir as loadMemoriesFromDir35,
10989
- resolveHaivePaths as resolveHaivePaths39
11181
+ resolveHaivePaths as resolveHaivePaths40
10990
11182
  } from "@hiveai/core";
10991
11183
  async function lintMemoriesAsync(root) {
10992
- const paths = resolveHaivePaths39(root);
11184
+ const paths = resolveHaivePaths40(root);
10993
11185
  const out = [];
10994
11186
  if (!existsSync64(paths.memoriesDir)) return out;
10995
11187
  const loaded = await loadMemoriesFromDir35(paths.memoriesDir);
@@ -11052,7 +11244,7 @@ function registerMemoryLint(parent) {
11052
11244
  parent.command("lint").description(
11053
11245
  "Heuristic corpus checks (anchors on key types, headings, verbosity). Static analysis only."
11054
11246
  ).option("--json", "emit findings as JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11055
- const root = findProjectRoot43(opts.dir);
11247
+ const root = findProjectRoot44(opts.dir);
11056
11248
  const findings = await lintMemoriesAsync(root);
11057
11249
  if (opts.json) {
11058
11250
  console.log(JSON.stringify({ findings_count: findings.length, findings }, null, 2));
@@ -11098,27 +11290,27 @@ function registerMemorySuggestTopic(memory2) {
11098
11290
  }
11099
11291
 
11100
11292
  // src/commands/resolve-project.ts
11101
- import path41 from "path";
11293
+ import path43 from "path";
11102
11294
  import "commander";
11103
11295
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
11104
11296
  function registerResolveProject(program2) {
11105
11297
  program2.command("resolve-project").description(
11106
11298
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
11107
11299
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
11108
- const info = resolveProjectInfo2({ cwd: path41.resolve(opts.dir) });
11300
+ const info = resolveProjectInfo2({ cwd: path43.resolve(opts.dir) });
11109
11301
  console.log(JSON.stringify({ ok: true, info }, null, 2));
11110
11302
  });
11111
11303
  }
11112
11304
 
11113
11305
  // src/commands/runtime-journal.ts
11114
11306
  import { existsSync as existsSync65 } from "fs";
11115
- import path43 from "path";
11307
+ import path44 from "path";
11116
11308
  import "commander";
11117
11309
  import {
11118
11310
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
11119
- findProjectRoot as findProjectRoot44,
11311
+ findProjectRoot as findProjectRoot45,
11120
11312
  readRuntimeJournalTail as readRuntimeJournalTail2,
11121
- resolveHaivePaths as resolveHaivePaths40
11313
+ resolveHaivePaths as resolveHaivePaths41
11122
11314
  } from "@hiveai/core";
11123
11315
  function registerRuntime(program2) {
11124
11316
  const runtime = program2.command("runtime").description(
@@ -11126,16 +11318,16 @@ function registerRuntime(program2) {
11126
11318
  );
11127
11319
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
11128
11320
  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) => {
11129
- const root = path43.resolve(opts.dir ?? process.cwd());
11130
- const paths = resolveHaivePaths40(findProjectRoot44(root));
11321
+ const root = path44.resolve(opts.dir ?? process.cwd());
11322
+ const paths = resolveHaivePaths41(findProjectRoot45(root));
11131
11323
  const raw = opts.kind ?? "note";
11132
11324
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
11133
11325
  await appendRuntimeJournalEntry3(paths, { kind, message });
11134
- ui.success(`Appended to ${path43.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
11326
+ ui.success(`Appended to ${path44.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
11135
11327
  });
11136
11328
  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) => {
11137
- const root = path43.resolve(opts.dir ?? process.cwd());
11138
- const paths = resolveHaivePaths40(findProjectRoot44(root));
11329
+ const root = path44.resolve(opts.dir ?? process.cwd());
11330
+ const paths = resolveHaivePaths41(findProjectRoot45(root));
11139
11331
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
11140
11332
  if (!existsSync65(paths.haiveDir)) {
11141
11333
  ui.error("No .ai/ \u2014 run `haive init` first.");
@@ -11153,12 +11345,12 @@ function registerRuntime(program2) {
11153
11345
 
11154
11346
  // src/commands/memory-timeline.ts
11155
11347
  import { existsSync as existsSync66 } from "fs";
11156
- import path44 from "path";
11348
+ import path45 from "path";
11157
11349
  import "commander";
11158
11350
  import {
11159
11351
  collectTimelineEntries as collectTimelineEntries2,
11160
- findProjectRoot as findProjectRoot45,
11161
- resolveHaivePaths as resolveHaivePaths41
11352
+ findProjectRoot as findProjectRoot46,
11353
+ resolveHaivePaths as resolveHaivePaths42
11162
11354
  } from "@hiveai/core";
11163
11355
  function registerMemoryTimeline(memory2) {
11164
11356
  memory2.command("timeline").description(
@@ -11169,8 +11361,8 @@ function registerMemoryTimeline(memory2) {
11169
11361
  process.exitCode = 1;
11170
11362
  return;
11171
11363
  }
11172
- const root = path44.resolve(opts.dir ?? process.cwd());
11173
- const paths = resolveHaivePaths41(findProjectRoot45(root));
11364
+ const root = path45.resolve(opts.dir ?? process.cwd());
11365
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
11174
11366
  if (!existsSync66(paths.memoriesDir)) {
11175
11367
  ui.error("No memories \u2014 run `haive init`.");
11176
11368
  process.exitCode = 1;
@@ -11190,13 +11382,13 @@ function registerMemoryTimeline(memory2) {
11190
11382
 
11191
11383
  // src/commands/memory-conflict-candidates.ts
11192
11384
  import { existsSync as existsSync67 } from "fs";
11193
- import path45 from "path";
11385
+ import path46 from "path";
11194
11386
  import "commander";
11195
11387
  import {
11196
11388
  findLexicalConflictPairs as findLexicalConflictPairs2,
11197
11389
  findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
11198
- findProjectRoot as findProjectRoot46,
11199
- resolveHaivePaths as resolveHaivePaths42
11390
+ findProjectRoot as findProjectRoot47,
11391
+ resolveHaivePaths as resolveHaivePaths43
11200
11392
  } from "@hiveai/core";
11201
11393
  function parseTypes(csv) {
11202
11394
  const allowed = ["decision", "architecture", "convention", "gotcha"];
@@ -11212,8 +11404,8 @@ function registerMemoryConflictCandidates(memory2) {
11212
11404
  "decision,architecture,convention,gotcha (lexical scan)",
11213
11405
  "decision,architecture"
11214
11406
  ).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) => {
11215
- const root = path45.resolve(opts.dir ?? process.cwd());
11216
- const paths = resolveHaivePaths42(findProjectRoot46(root));
11407
+ const root = path46.resolve(opts.dir ?? process.cwd());
11408
+ const paths = resolveHaivePaths43(findProjectRoot47(root));
11217
11409
  if (!existsSync67(paths.memoriesDir)) {
11218
11410
  ui.error("No memories \u2014 run `haive init`.");
11219
11411
  process.exitCode = 1;
@@ -11251,11 +11443,11 @@ function registerMemoryConflictCandidates(memory2) {
11251
11443
  // src/commands/enforce.ts
11252
11444
  import { spawn as spawn5 } from "child_process";
11253
11445
  import { existsSync as existsSync68 } from "fs";
11254
- import { chmod as chmod2, mkdir as mkdir18, readFile as readFile17, rm as rm3, writeFile as writeFile31 } from "fs/promises";
11255
- import path46 from "path";
11446
+ import { chmod as chmod2, mkdir as mkdir19, readFile as readFile17, rm as rm3, writeFile as writeFile31 } from "fs/promises";
11447
+ import path47 from "path";
11256
11448
  import "commander";
11257
11449
  import {
11258
- findProjectRoot as findProjectRoot47,
11450
+ findProjectRoot as findProjectRoot48,
11259
11451
  hasRecentBriefingMarker,
11260
11452
  isFreshIsoDate,
11261
11453
  loadConfig as loadConfig8,
@@ -11263,7 +11455,7 @@ import {
11263
11455
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
11264
11456
  readRecentBriefingMarker,
11265
11457
  resolveBriefingBudget as resolveBriefingBudget3,
11266
- resolveHaivePaths as resolveHaivePaths43,
11458
+ resolveHaivePaths as resolveHaivePaths44,
11267
11459
  saveConfig as saveConfig3,
11268
11460
  SESSION_RECAP_TTL_MS,
11269
11461
  verifyAnchor as verifyAnchor4,
@@ -11276,9 +11468,9 @@ function registerEnforce(program2) {
11276
11468
  "Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
11277
11469
  );
11278
11470
  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) => {
11279
- const root = findProjectRoot47(opts.dir);
11280
- const paths = resolveHaivePaths43(root);
11281
- await mkdir18(paths.haiveDir, { recursive: true });
11471
+ const root = findProjectRoot48(opts.dir);
11472
+ const paths = resolveHaivePaths44(root);
11473
+ await mkdir19(paths.haiveDir, { recursive: true });
11282
11474
  const current = await loadConfig8(paths);
11283
11475
  await saveConfig3(paths, {
11284
11476
  ...current,
@@ -11302,7 +11494,7 @@ function registerEnforce(program2) {
11302
11494
  if (opts.claude !== false) {
11303
11495
  try {
11304
11496
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
11305
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path46.relative(root, result.settingsPath)})`);
11497
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path47.relative(root, result.settingsPath)})`);
11306
11498
  } catch (err) {
11307
11499
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
11308
11500
  }
@@ -11321,15 +11513,15 @@ function registerEnforce(program2) {
11321
11513
  if (report.should_block) process.exit(2);
11322
11514
  });
11323
11515
  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) => {
11324
- const root = findProjectRoot47(opts.dir);
11325
- const paths = resolveHaivePaths43(root);
11516
+ const root = findProjectRoot48(opts.dir);
11517
+ const paths = resolveHaivePaths44(root);
11326
11518
  const targets = [
11327
- path46.join(paths.haiveDir, ".cache"),
11328
- path46.join(paths.haiveDir, ".runtime")
11519
+ path47.join(paths.haiveDir, ".cache"),
11520
+ path47.join(paths.haiveDir, ".runtime")
11329
11521
  ];
11330
11522
  for (const target of targets) {
11331
11523
  if (!existsSync68(target)) continue;
11332
- const rel = path46.relative(root, target);
11524
+ const rel = path47.relative(root, target);
11333
11525
  if (opts.dryRun) ui.info(`would remove ${rel}`);
11334
11526
  else {
11335
11527
  await rm3(target, { recursive: true, force: true });
@@ -11346,9 +11538,9 @@ function registerEnforce(program2) {
11346
11538
  const payload = await readHookPayload();
11347
11539
  const root = resolveRoot(opts.dir, payload);
11348
11540
  if (!root) return;
11349
- const paths = resolveHaivePaths43(root);
11541
+ const paths = resolveHaivePaths44(root);
11350
11542
  if (!existsSync68(paths.haiveDir)) return;
11351
- await mkdir18(paths.runtimeDir, { recursive: true });
11543
+ await mkdir19(paths.runtimeDir, { recursive: true });
11352
11544
  const sessionId = opts.sessionId ?? payload.session_id;
11353
11545
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
11354
11546
  const budget = resolveBriefingBudget3("quick", {
@@ -11408,7 +11600,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
11408
11600
  const payload = await readHookPayload();
11409
11601
  const root = resolveRoot(opts.dir, payload);
11410
11602
  if (!root) return;
11411
- const paths = resolveHaivePaths43(root);
11603
+ const paths = resolveHaivePaths44(root);
11412
11604
  if (!existsSync68(paths.haiveDir)) return;
11413
11605
  if (!isWriteLikeTool(payload)) return;
11414
11606
  const ok = await hasRecentBriefingMarker(paths, payload.session_id);
@@ -11431,8 +11623,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
11431
11623
  });
11432
11624
  }
11433
11625
  async function runWithEnforcement(command, args, opts) {
11434
- const root = findProjectRoot47(opts.dir);
11435
- const paths = resolveHaivePaths43(root);
11626
+ const root = findProjectRoot48(opts.dir);
11627
+ const paths = resolveHaivePaths44(root);
11436
11628
  if (!existsSync68(paths.haiveDir)) {
11437
11629
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11438
11630
  process.exit(1);
@@ -11452,7 +11644,7 @@ async function runWithEnforcement(command, args, opts) {
11452
11644
  process.exit(2);
11453
11645
  }
11454
11646
  ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
11455
- ui.info(`Briefing written to ${path46.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
11647
+ ui.info(`Briefing written to ${path47.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
11456
11648
  const child = spawn5(command, args, {
11457
11649
  cwd: root,
11458
11650
  stdio: "inherit",
@@ -11501,9 +11693,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
11501
11693
  source: "haive-run",
11502
11694
  memoryIds: briefing.memories.map((m) => m.id)
11503
11695
  });
11504
- const dir = path46.join(paths.runtimeDir, "enforcement", "briefings");
11505
- await mkdir18(dir, { recursive: true });
11506
- const file = path46.join(dir, `${sessionId}.md`);
11696
+ const dir = path47.join(paths.runtimeDir, "enforcement", "briefings");
11697
+ await mkdir19(dir, { recursive: true });
11698
+ const file = path47.join(dir, `${sessionId}.md`);
11507
11699
  const parts = [
11508
11700
  "# hAIve Briefing",
11509
11701
  "",
@@ -11525,8 +11717,8 @@ async function writeWrapperBriefing(paths, sessionId, task) {
11525
11717
  return file;
11526
11718
  }
11527
11719
  async function buildEnforcementReport(dir, stage, sessionId) {
11528
- const root = findProjectRoot47(dir);
11529
- const paths = resolveHaivePaths43(root);
11720
+ const root = findProjectRoot48(dir);
11721
+ const paths = resolveHaivePaths44(root);
11530
11722
  const initialized = existsSync68(paths.haiveDir);
11531
11723
  const config = initialized ? await loadConfig8(paths) : {};
11532
11724
  const mode = config.enforcement?.mode ?? "strict";
@@ -11618,9 +11810,11 @@ async function buildEnforcementReport(dir, stage, sessionId) {
11618
11810
  async function hasRecentSessionRecap(paths) {
11619
11811
  if (!existsSync68(paths.memoriesDir)) return false;
11620
11812
  const all = await loadMemoriesFromDir36(paths.memoriesDir);
11621
- return all.some(
11622
- ({ memory: memory2 }) => memory2.frontmatter.type === "session_recap" && memory2.frontmatter.status !== "rejected" && isFreshIsoDate(memory2.frontmatter.created_at, SESSION_RECAP_TTL_MS)
11623
- );
11813
+ return all.some(({ memory: memory2 }) => {
11814
+ const fm = memory2.frontmatter;
11815
+ const freshnessDate = fm.verified_at ?? fm.created_at;
11816
+ return fm.type === "session_recap" && fm.status !== "rejected" && isFreshIsoDate(freshnessDate, SESSION_RECAP_TTL_MS);
11817
+ });
11624
11818
  }
11625
11819
  async function verifyMemoryPolicy(paths, config) {
11626
11820
  if (!existsSync68(paths.memoriesDir)) return [];
@@ -11722,7 +11916,7 @@ async function runPrecommitPolicy(paths) {
11722
11916
  return [{
11723
11917
  severity: "error",
11724
11918
  code: "precommit-policy-block",
11725
- message: `Pre-commit policy matched ${result.summary.anti_patterns} anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
11919
+ message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
11726
11920
  fix: "Review the hAIve warnings, then update the code or the relevant memories.",
11727
11921
  impact: 45
11728
11922
  }];
@@ -11777,12 +11971,12 @@ function buildScore(findings, threshold = 80) {
11777
11971
  };
11778
11972
  }
11779
11973
  async function installGitEnforcement(root) {
11780
- const hooksDir = path46.join(root, ".git", "hooks");
11781
- if (!existsSync68(path46.join(root, ".git"))) {
11974
+ const hooksDir = path47.join(root, ".git", "hooks");
11975
+ if (!existsSync68(path47.join(root, ".git"))) {
11782
11976
  ui.warn("No .git directory found; git enforcement hooks skipped.");
11783
11977
  return;
11784
11978
  }
11785
- await mkdir18(hooksDir, { recursive: true });
11979
+ await mkdir19(hooksDir, { recursive: true });
11786
11980
  const hooks = [
11787
11981
  {
11788
11982
  name: "pre-commit",
@@ -11800,7 +11994,7 @@ haive enforce check --stage pre-push --dir . || exit $?
11800
11994
  }
11801
11995
  ];
11802
11996
  for (const hook of hooks) {
11803
- const file = path46.join(hooksDir, hook.name);
11997
+ const file = path47.join(hooksDir, hook.name);
11804
11998
  if (existsSync68(file)) {
11805
11999
  const current = await readFile17(file, "utf8").catch(() => "");
11806
12000
  if (current.includes(ENFORCE_HOOK_MARKER)) {
@@ -11818,8 +12012,8 @@ ${hook.body}`, "utf8");
11818
12012
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
11819
12013
  }
11820
12014
  async function installCiEnforcement(root) {
11821
- const workflowPath = path46.join(root, ".github", "workflows", "haive-enforcement.yml");
11822
- await mkdir18(path46.dirname(workflowPath), { recursive: true });
12015
+ const workflowPath = path47.join(root, ".github", "workflows", "haive-enforcement.yml");
12016
+ await mkdir19(path47.dirname(workflowPath), { recursive: true });
11823
12017
  if (existsSync68(workflowPath)) {
11824
12018
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
11825
12019
  return;
@@ -11848,7 +12042,7 @@ jobs:
11848
12042
  - name: Enforce hAIve policy
11849
12043
  run: haive enforce ci
11850
12044
  `, "utf8");
11851
- ui.success(`Created ${path46.relative(root, workflowPath)}`);
12045
+ ui.success(`Created ${path47.relative(root, workflowPath)}`);
11852
12046
  }
11853
12047
  function printReport(report, json) {
11854
12048
  if (json) {
@@ -11877,7 +12071,7 @@ async function readHookPayload() {
11877
12071
  }
11878
12072
  function resolveRoot(dir, payload) {
11879
12073
  try {
11880
- return findProjectRoot47(dir ?? payload.cwd);
12074
+ return findProjectRoot48(dir ?? payload.cwd);
11881
12075
  } catch {
11882
12076
  return null;
11883
12077
  }
@@ -11946,14 +12140,15 @@ function registerRun(program2) {
11946
12140
  }
11947
12141
 
11948
12142
  // src/index.ts
11949
- var program = new Command50();
11950
- program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.11");
12143
+ var program = new Command51();
12144
+ program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.13");
11951
12145
  registerInit(program);
11952
12146
  registerWelcome(program);
11953
12147
  registerResolveProject(program);
11954
12148
  registerRuntime(program);
11955
12149
  registerEnforce(program);
11956
12150
  registerRun(program);
12151
+ registerAgent(program);
11957
12152
  registerMcp(program);
11958
12153
  registerBriefing(program);
11959
12154
  registerTui(program);