@hiveai/cli 0.9.11 → 0.9.12

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 = {
@@ -6082,7 +6268,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
6082
6268
  };
6083
6269
  }
6084
6270
  var SERVER_NAME = "haive";
6085
- var SERVER_VERSION = "0.9.11";
6271
+ var SERVER_VERSION = "0.9.12";
6086
6272
  function jsonResult(data) {
6087
6273
  return {
6088
6274
  content: [
@@ -6991,7 +7177,7 @@ function registerMcp(program2) {
6991
7177
  ).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
7178
  void opts.stdio;
6993
7179
  const raw = opts.root ?? opts.dir;
6994
- const root = raw ? findProjectRoot9(raw) : findProjectRoot9();
7180
+ const root = raw ? findProjectRoot10(raw) : findProjectRoot10();
6995
7181
  try {
6996
7182
  await runHaiveMcpStdio({ root });
6997
7183
  } catch (err) {
@@ -7002,15 +7188,15 @@ function registerMcp(program2) {
7002
7188
  }
7003
7189
 
7004
7190
  // 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";
7191
+ import { spawnSync as spawnSync3 } from "child_process";
7192
+ import { readFile as readFile8, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
7007
7193
  import { existsSync as existsSync29 } from "fs";
7008
- import path12 from "path";
7194
+ import path13 from "path";
7009
7195
  import "commander";
7010
7196
  import {
7011
7197
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
7012
7198
  buildFrontmatter as buildFrontmatter6,
7013
- findProjectRoot as findProjectRoot10,
7199
+ findProjectRoot as findProjectRoot11,
7014
7200
  getUsage as getUsage10,
7015
7201
  isAutoPromoteEligible as isAutoPromoteEligible2,
7016
7202
  isDecaying as isDecaying2,
@@ -7019,7 +7205,7 @@ import {
7019
7205
  loadMemoriesFromDir as loadMemoriesFromDir23,
7020
7206
  loadUsageIndex as loadUsageIndex12,
7021
7207
  pullCrossRepoSources,
7022
- resolveHaivePaths as resolveHaivePaths7,
7208
+ resolveHaivePaths as resolveHaivePaths8,
7023
7209
  resolveManifestFiles,
7024
7210
  serializeMemory as serializeMemory11,
7025
7211
  trackDependencies,
@@ -7038,8 +7224,8 @@ function registerSync(program2) {
7038
7224
  "--inject-bridge",
7039
7225
  "inject top validated memories into CLAUDE.md (or --bridge-file) between <!-- haive:memories-start/end --> markers"
7040
7226
  ).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);
7227
+ const root = findProjectRoot11(opts.dir);
7228
+ const paths = resolveHaivePaths8(root);
7043
7229
  if (!existsSync29(paths.memoriesDir)) {
7044
7230
  if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
7045
7231
  process.exitCode = 1;
@@ -7172,7 +7358,7 @@ function registerSync(program2) {
7172
7358
  );
7173
7359
  }
7174
7360
  if (opts.injectBridge) {
7175
- const bridgeFile = opts.bridgeFile ? path12.resolve(opts.bridgeFile) : path12.join(root, "CLAUDE.md");
7361
+ const bridgeFile = opts.bridgeFile ? path13.resolve(opts.bridgeFile) : path13.join(root, "CLAUDE.md");
7176
7362
  const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
7177
7363
  await injectBridge(bridgeFile, paths.memoriesDir, maxInject, root, opts.quiet);
7178
7364
  }
@@ -7278,10 +7464,10 @@ Attends une **confirmation explicite** avant d'agir.
7278
7464
  paths: [result.file],
7279
7465
  topic: `dep-bump-${slugParts}`
7280
7466
  });
7281
- const teamDir = path12.join(paths.memoriesDir, "team");
7282
- await mkdir9(teamDir, { recursive: true });
7467
+ const teamDir = path13.join(paths.memoriesDir, "team");
7468
+ await mkdir10(teamDir, { recursive: true });
7283
7469
  await writeFile13(
7284
- path12.join(teamDir, `${fm.id}.md`),
7470
+ path13.join(teamDir, `${fm.id}.md`),
7285
7471
  serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
7286
7472
  "utf8"
7287
7473
  );
@@ -7345,10 +7531,10 @@ Attends une **confirmation explicite** avant d'agir.
7345
7531
  paths: [diff.file],
7346
7532
  topic: `contract-breaking-${diff.contract}`
7347
7533
  });
7348
- const teamDir = path12.join(paths.memoriesDir, "team");
7349
- await mkdir9(teamDir, { recursive: true });
7534
+ const teamDir = path13.join(paths.memoriesDir, "team");
7535
+ await mkdir10(teamDir, { recursive: true });
7350
7536
  await writeFile13(
7351
- path12.join(teamDir, `${fm.id}.md`),
7537
+ path13.join(teamDir, `${fm.id}.md`),
7352
7538
  serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
7353
7539
  "utf8"
7354
7540
  );
@@ -7362,7 +7548,7 @@ Attends une **confirmation explicite** avant d'agir.
7362
7548
  const existingMap = await loadCodeMap4(paths);
7363
7549
  if (existingMap) {
7364
7550
  const mapAge = new Date(existingMap.generated_at).getTime();
7365
- const gitResult = spawnSync2(
7551
+ const gitResult = spawnSync3(
7366
7552
  "git",
7367
7553
  [
7368
7554
  "diff",
@@ -7441,11 +7627,11 @@ ${BRIDGE_END}`;
7441
7627
  const startIdx = existing.indexOf(BRIDGE_START);
7442
7628
  const endIdx = existing.indexOf(BRIDGE_END);
7443
7629
  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.`);
7630
+ ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
7445
7631
  return;
7446
7632
  }
7447
7633
  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.`);
7634
+ ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
7449
7635
  return;
7450
7636
  }
7451
7637
  let updated;
@@ -7453,19 +7639,19 @@ ${BRIDGE_END}`;
7453
7639
  updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
7454
7640
  } else {
7455
7641
  if (!fileExists && !quiet) {
7456
- ui.info(`Creating ${path12.relative(root, bridgeFile)} with haive memory block.`);
7642
+ ui.info(`Creating ${path13.relative(root, bridgeFile)} with haive memory block.`);
7457
7643
  }
7458
7644
  updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
7459
7645
  }
7460
7646
  await writeFile13(bridgeFile, updated, "utf8");
7461
7647
  if (!quiet) {
7462
7648
  console.log(
7463
- ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path12.relative(root, bridgeFile)}`)
7649
+ ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path13.relative(root, bridgeFile)}`)
7464
7650
  );
7465
7651
  }
7466
7652
  }
7467
7653
  function collectSinceChanges(root, ref) {
7468
- const result = spawnSync2(
7654
+ const result = spawnSync3(
7469
7655
  "git",
7470
7656
  ["-C", root, "diff", "--name-status", "--diff-filter=AMD", `${ref}...HEAD`, "--", ".ai/memories"],
7471
7657
  { encoding: "utf8" }
@@ -7485,17 +7671,17 @@ function collectSinceChanges(root, ref) {
7485
7671
 
7486
7672
  // src/commands/memory-add.ts
7487
7673
  import { createHash as createHash2 } from "crypto";
7488
- import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
7674
+ import { mkdir as mkdir11, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
7489
7675
  import { existsSync as existsSync30 } from "fs";
7490
- import path13 from "path";
7676
+ import path14 from "path";
7491
7677
  import "commander";
7492
7678
  import {
7493
7679
  buildFrontmatter as buildFrontmatter7,
7494
- findProjectRoot as findProjectRoot11,
7680
+ findProjectRoot as findProjectRoot12,
7495
7681
  inferModulesFromPaths as inferModulesFromPaths3,
7496
7682
  loadMemoriesFromDir as loadMemoriesFromDir24,
7497
7683
  memoryFilePath as memoryFilePath6,
7498
- resolveHaivePaths as resolveHaivePaths8,
7684
+ resolveHaivePaths as resolveHaivePaths9,
7499
7685
  serializeMemory as serializeMemory12
7500
7686
  } from "@hiveai/core";
7501
7687
  function registerMemoryAdd(memory2) {
@@ -7523,8 +7709,8 @@ function registerMemoryAdd(memory2) {
7523
7709
  --scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
7524
7710
  `
7525
7711
  ).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);
7712
+ const root = findProjectRoot12(opts.dir);
7713
+ const paths = resolveHaivePaths9(root);
7528
7714
  if (!existsSync30(paths.haiveDir)) {
7529
7715
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
7530
7716
  process.exitCode = 1;
@@ -7536,7 +7722,7 @@ function registerMemoryAdd(memory2) {
7536
7722
  const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
7537
7723
  const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
7538
7724
  if (anchorPaths.length > 0) {
7539
- const missing = anchorPaths.filter((p) => !existsSync30(path13.resolve(root, p)));
7725
+ const missing = anchorPaths.filter((p) => !existsSync30(path14.resolve(root, p)));
7540
7726
  if (missing.length > 0) {
7541
7727
  ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
7542
7728
  for (const p of missing) ui.warn(` \u2717 ${p}`);
@@ -7601,7 +7787,7 @@ TODO \u2014 write the memory body.
7601
7787
  }
7602
7788
  };
7603
7789
  await writeFile14(topicMatch.filePath, serializeMemory12({ frontmatter: newFrontmatter, body }), "utf8");
7604
- ui.success(`Updated (topic upsert) ${path13.relative(root, topicMatch.filePath)}`);
7790
+ ui.success(`Updated (topic upsert) ${path14.relative(root, topicMatch.filePath)}`);
7605
7791
  ui.info(`id=${fm.id} revision=${revisionCount}`);
7606
7792
  return;
7607
7793
  }
@@ -7620,7 +7806,7 @@ TODO \u2014 write the memory body.
7620
7806
  topic: opts.topic
7621
7807
  });
7622
7808
  const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
7623
- await mkdir10(path13.dirname(file), { recursive: true });
7809
+ await mkdir11(path14.dirname(file), { recursive: true });
7624
7810
  if (existsSync30(file)) {
7625
7811
  ui.error(`Memory already exists at ${file}`);
7626
7812
  process.exitCode = 1;
@@ -7639,7 +7825,7 @@ TODO \u2014 write the memory body.
7639
7825
  }
7640
7826
  }
7641
7827
  await writeFile14(file, serializeMemory12({ frontmatter, body }), "utf8");
7642
- ui.success(`Created ${path13.relative(root, file)}`);
7828
+ ui.success(`Created ${path14.relative(root, file)}`);
7643
7829
  ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
7644
7830
  if (inferredTags.length > 0) {
7645
7831
  ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
@@ -7670,9 +7856,9 @@ function parseCsv2(value) {
7670
7856
 
7671
7857
  // src/commands/memory-list.ts
7672
7858
  import { existsSync as existsSync31 } from "fs";
7673
- import path14 from "path";
7859
+ import path15 from "path";
7674
7860
  import "commander";
7675
- import { findProjectRoot as findProjectRoot12, resolveHaivePaths as resolveHaivePaths9 } from "@hiveai/core";
7861
+ import { findProjectRoot as findProjectRoot13, resolveHaivePaths as resolveHaivePaths10 } from "@hiveai/core";
7676
7862
 
7677
7863
  // src/utils/fs.ts
7678
7864
  import {
@@ -7684,8 +7870,8 @@ import {
7684
7870
  // src/commands/memory-list.ts
7685
7871
  function registerMemoryList(memory2) {
7686
7872
  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);
7873
+ const root = findProjectRoot13(opts.dir);
7874
+ const paths = resolveHaivePaths10(root);
7689
7875
  if (!existsSync31(paths.memoriesDir)) {
7690
7876
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
7691
7877
  process.exitCode = 1;
@@ -7718,7 +7904,7 @@ function registerMemoryList(memory2) {
7718
7904
  console.log(
7719
7905
  `${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
7720
7906
  );
7721
- console.log(` ${ui.dim(path14.relative(root, filePath))}`);
7907
+ console.log(` ${ui.dim(path15.relative(root, filePath))}`);
7722
7908
  }
7723
7909
  console.log(ui.dim(`
7724
7910
  ${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
@@ -7753,20 +7939,20 @@ function matchesFilters(loaded, opts) {
7753
7939
  }
7754
7940
 
7755
7941
  // src/commands/memory-promote.ts
7756
- import { mkdir as mkdir11, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
7942
+ import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
7757
7943
  import { existsSync as existsSync33 } from "fs";
7758
- import path15 from "path";
7944
+ import path16 from "path";
7759
7945
  import "commander";
7760
7946
  import {
7761
- findProjectRoot as findProjectRoot13,
7947
+ findProjectRoot as findProjectRoot14,
7762
7948
  memoryFilePath as memoryFilePath7,
7763
- resolveHaivePaths as resolveHaivePaths10,
7949
+ resolveHaivePaths as resolveHaivePaths11,
7764
7950
  serializeMemory as serializeMemory13
7765
7951
  } from "@hiveai/core";
7766
7952
  function registerMemoryPromote(memory2) {
7767
7953
  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);
7954
+ const root = findProjectRoot14(opts.dir);
7955
+ const paths = resolveHaivePaths11(root);
7770
7956
  if (!existsSync33(paths.memoriesDir)) {
7771
7957
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
7772
7958
  process.exitCode = 1;
@@ -7802,11 +7988,11 @@ function registerMemoryPromote(memory2) {
7802
7988
  body: found.memory.body
7803
7989
  };
7804
7990
  const newPath = memoryFilePath7(paths, "team", updated.frontmatter.id);
7805
- await mkdir11(path15.dirname(newPath), { recursive: true });
7991
+ await mkdir12(path16.dirname(newPath), { recursive: true });
7806
7992
  await writeFile15(newPath, serializeMemory13(updated), "utf8");
7807
7993
  await unlink2(found.filePath);
7808
7994
  ui.success(`Promoted ${id} to team scope (status=proposed)`);
7809
- ui.info(`Now at ${path15.relative(root, newPath)}`);
7995
+ ui.info(`Now at ${path16.relative(root, newPath)}`);
7810
7996
  console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
7811
7997
  });
7812
7998
  }
@@ -7814,17 +8000,17 @@ function registerMemoryPromote(memory2) {
7814
8000
  // src/commands/memory-approve.ts
7815
8001
  import { existsSync as existsSync34 } from "fs";
7816
8002
  import { writeFile as writeFile16 } from "fs/promises";
7817
- import path16 from "path";
8003
+ import path17 from "path";
7818
8004
  import "commander";
7819
8005
  import {
7820
- findProjectRoot as findProjectRoot14,
7821
- resolveHaivePaths as resolveHaivePaths11,
8006
+ findProjectRoot as findProjectRoot15,
8007
+ resolveHaivePaths as resolveHaivePaths12,
7822
8008
  serializeMemory as serializeMemory14
7823
8009
  } from "@hiveai/core";
7824
8010
  function registerMemoryApprove(memory2) {
7825
8011
  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);
8012
+ const root = findProjectRoot15(opts.dir);
8013
+ const paths = resolveHaivePaths12(root);
7828
8014
  if (!existsSync34(paths.memoriesDir)) {
7829
8015
  ui.error(`No .ai/memories at ${root}.`);
7830
8016
  process.exitCode = 1;
@@ -7878,24 +8064,24 @@ function registerMemoryApprove(memory2) {
7878
8064
  };
7879
8065
  await writeFile16(found.filePath, serializeMemory14(next), "utf8");
7880
8066
  ui.success(`Approved ${id} (status=validated)`);
7881
- ui.info(path16.relative(root, found.filePath));
8067
+ ui.info(path17.relative(root, found.filePath));
7882
8068
  });
7883
8069
  }
7884
8070
 
7885
8071
  // src/commands/memory-update.ts
7886
8072
  import { writeFile as writeFile17 } from "fs/promises";
7887
8073
  import { existsSync as existsSync35 } from "fs";
7888
- import path17 from "path";
8074
+ import path18 from "path";
7889
8075
  import "commander";
7890
8076
  import {
7891
- findProjectRoot as findProjectRoot15,
7892
- resolveHaivePaths as resolveHaivePaths12,
8077
+ findProjectRoot as findProjectRoot16,
8078
+ resolveHaivePaths as resolveHaivePaths13,
7893
8079
  serializeMemory as serializeMemory15
7894
8080
  } from "@hiveai/core";
7895
8081
  function registerMemoryUpdate(memory2) {
7896
8082
  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);
8083
+ const root = findProjectRoot16(opts.dir);
8084
+ const paths = resolveHaivePaths13(root);
7899
8085
  if (!existsSync35(paths.memoriesDir)) {
7900
8086
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
7901
8087
  process.exitCode = 1;
@@ -7948,7 +8134,7 @@ function registerMemoryUpdate(memory2) {
7948
8134
  serializeMemory15({ frontmatter: newFrontmatter, body: newBody }),
7949
8135
  "utf8"
7950
8136
  );
7951
- ui.success(`Updated ${path17.relative(root, loaded.filePath)}`);
8137
+ ui.success(`Updated ${path18.relative(root, loaded.filePath)}`);
7952
8138
  ui.info(`fields: ${updated.join(", ")}`);
7953
8139
  });
7954
8140
  }
@@ -7969,15 +8155,15 @@ function parseCsv3(value) {
7969
8155
  // src/commands/memory-auto-promote.ts
7970
8156
  import { writeFile as writeFile18 } from "fs/promises";
7971
8157
  import { existsSync as existsSync36 } from "fs";
7972
- import path18 from "path";
8158
+ import path19 from "path";
7973
8159
  import "commander";
7974
8160
  import {
7975
8161
  DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
7976
- findProjectRoot as findProjectRoot16,
8162
+ findProjectRoot as findProjectRoot17,
7977
8163
  getUsage as getUsage11,
7978
8164
  isAutoPromoteEligible as isAutoPromoteEligible3,
7979
8165
  loadUsageIndex as loadUsageIndex13,
7980
- resolveHaivePaths as resolveHaivePaths13,
8166
+ resolveHaivePaths as resolveHaivePaths14,
7981
8167
  serializeMemory as serializeMemory16
7982
8168
  } from "@hiveai/core";
7983
8169
  function registerMemoryAutoPromote(memory2) {
@@ -7986,8 +8172,8 @@ function registerMemoryAutoPromote(memory2) {
7986
8172
  "memories with more rejections than this are skipped",
7987
8173
  String(DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
7988
8174
  ).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);
8175
+ const root = findProjectRoot17(opts.dir);
8176
+ const paths = resolveHaivePaths14(root);
7991
8177
  if (!existsSync36(paths.memoriesDir)) {
7992
8178
  ui.error(`No .ai/memories at ${root}.`);
7993
8179
  process.exitCode = 1;
@@ -8014,7 +8200,7 @@ function registerMemoryAutoPromote(memory2) {
8014
8200
  console.log(
8015
8201
  `${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
8016
8202
  );
8017
- console.log(` ${ui.dim(path18.relative(root, filePath))}`);
8203
+ console.log(` ${ui.dim(path19.relative(root, filePath))}`);
8018
8204
  if (opts.apply) {
8019
8205
  const next = {
8020
8206
  frontmatter: { ...mem.frontmatter, status: "validated" },
@@ -8033,17 +8219,17 @@ function registerMemoryAutoPromote(memory2) {
8033
8219
  import { spawn as spawn3 } from "child_process";
8034
8220
  import { existsSync as existsSync37 } from "fs";
8035
8221
  import { readFile as readFile10 } from "fs/promises";
8036
- import path19 from "path";
8222
+ import path20 from "path";
8037
8223
  import "commander";
8038
8224
  import {
8039
- findProjectRoot as findProjectRoot17,
8225
+ findProjectRoot as findProjectRoot18,
8040
8226
  parseMemory,
8041
- resolveHaivePaths as resolveHaivePaths14
8227
+ resolveHaivePaths as resolveHaivePaths15
8042
8228
  } from "@hiveai/core";
8043
8229
  function registerMemoryEdit(memory2) {
8044
8230
  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);
8231
+ const root = findProjectRoot18(opts.dir);
8232
+ const paths = resolveHaivePaths15(root);
8047
8233
  if (!existsSync37(paths.memoriesDir)) {
8048
8234
  ui.error(`No .ai/memories at ${root}.`);
8049
8235
  process.exitCode = 1;
@@ -8057,7 +8243,7 @@ function registerMemoryEdit(memory2) {
8057
8243
  return;
8058
8244
  }
8059
8245
  const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
8060
- ui.info(`Opening ${path19.relative(root, found.filePath)} with ${editor}\u2026`);
8246
+ ui.info(`Opening ${path20.relative(root, found.filePath)} with ${editor}\u2026`);
8061
8247
  const code = await runEditor(editor, found.filePath);
8062
8248
  if (code !== 0) {
8063
8249
  ui.warn(`Editor exited with status ${code}.`);
@@ -8085,21 +8271,21 @@ function runEditor(editor, file) {
8085
8271
 
8086
8272
  // src/commands/memory-for-files.ts
8087
8273
  import { existsSync as existsSync38 } from "fs";
8088
- import path20 from "path";
8274
+ import path21 from "path";
8089
8275
  import "commander";
8090
8276
  import {
8091
8277
  deriveConfidence as deriveConfidence9,
8092
- findProjectRoot as findProjectRoot18,
8278
+ findProjectRoot as findProjectRoot19,
8093
8279
  getUsage as getUsage12,
8094
8280
  inferModulesFromPaths as inferModulesFromPaths4,
8095
8281
  loadUsageIndex as loadUsageIndex14,
8096
8282
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths5,
8097
- resolveHaivePaths as resolveHaivePaths15
8283
+ resolveHaivePaths as resolveHaivePaths16
8098
8284
  } from "@hiveai/core";
8099
8285
  function registerMemoryForFiles(memory2) {
8100
8286
  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);
8287
+ const root = findProjectRoot19(opts.dir);
8288
+ const paths = resolveHaivePaths16(root);
8103
8289
  if (!existsSync38(paths.memoriesDir)) {
8104
8290
  ui.error(`No .ai/memories at ${root}.`);
8105
8291
  process.exitCode = 1;
@@ -8207,24 +8393,24 @@ function printGroup(root, label, loaded, usage) {
8207
8393
  const u = getUsage12(usage, fm.id);
8208
8394
  const conf = deriveConfidence9(fm, u);
8209
8395
  console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
8210
- console.log(` ${ui.dim(path20.relative(root, filePath))}`);
8396
+ console.log(` ${ui.dim(path21.relative(root, filePath))}`);
8211
8397
  }
8212
8398
  }
8213
8399
 
8214
8400
  // src/commands/memory-hot.ts
8215
8401
  import { existsSync as existsSync39 } from "fs";
8216
- import path21 from "path";
8402
+ import path23 from "path";
8217
8403
  import "commander";
8218
8404
  import {
8219
- findProjectRoot as findProjectRoot19,
8405
+ findProjectRoot as findProjectRoot20,
8220
8406
  getUsage as getUsage13,
8221
8407
  loadUsageIndex as loadUsageIndex15,
8222
- resolveHaivePaths as resolveHaivePaths16
8408
+ resolveHaivePaths as resolveHaivePaths17
8223
8409
  } from "@hiveai/core";
8224
8410
  function registerMemoryHot(memory2) {
8225
8411
  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);
8412
+ const root = findProjectRoot20(opts.dir);
8413
+ const paths = resolveHaivePaths17(root);
8228
8414
  if (!existsSync39(paths.memoriesDir)) {
8229
8415
  ui.error(`No .ai/memories at ${root}.`);
8230
8416
  process.exitCode = 1;
@@ -8253,7 +8439,7 @@ function registerMemoryHot(memory2) {
8253
8439
  console.log(
8254
8440
  `${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
8441
  );
8256
- console.log(` ${ui.dim(path21.relative(root, filePath))}`);
8442
+ console.log(` ${ui.dim(path23.relative(root, filePath))}`);
8257
8443
  }
8258
8444
  ui.info(
8259
8445
  `${candidates.length} hot \u2014 promote drafts with \`haive memory promote <id>\`, then \`haive memory auto-promote --apply\`.`
@@ -8262,15 +8448,15 @@ function registerMemoryHot(memory2) {
8262
8448
  }
8263
8449
 
8264
8450
  // src/commands/memory-tried.ts
8265
- import { mkdir as mkdir12, writeFile as writeFile19 } from "fs/promises";
8451
+ import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
8266
8452
  import { existsSync as existsSync40 } from "fs";
8267
- import path23 from "path";
8453
+ import path24 from "path";
8268
8454
  import "commander";
8269
8455
  import {
8270
8456
  buildFrontmatter as buildFrontmatter8,
8271
- findProjectRoot as findProjectRoot20,
8457
+ findProjectRoot as findProjectRoot21,
8272
8458
  memoryFilePath as memoryFilePath8,
8273
- resolveHaivePaths as resolveHaivePaths17,
8459
+ resolveHaivePaths as resolveHaivePaths18,
8274
8460
  serializeMemory as serializeMemory17
8275
8461
  } from "@hiveai/core";
8276
8462
  function registerMemoryTried(memory2) {
@@ -8290,8 +8476,8 @@ function registerMemoryTried(memory2) {
8290
8476
  --paths packages/cli/src/index.ts
8291
8477
  `
8292
8478
  ).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);
8479
+ const root = findProjectRoot21(opts.dir);
8480
+ const paths = resolveHaivePaths18(root);
8295
8481
  if (!existsSync40(paths.haiveDir)) {
8296
8482
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
8297
8483
  process.exitCode = 1;
@@ -8315,14 +8501,14 @@ function registerMemoryTried(memory2) {
8315
8501
  }
8316
8502
  const body = lines.join("\n") + "\n";
8317
8503
  const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
8318
- await mkdir12(path23.dirname(file), { recursive: true });
8504
+ await mkdir13(path24.dirname(file), { recursive: true });
8319
8505
  if (existsSync40(file)) {
8320
8506
  ui.error(`Memory already exists at ${file}`);
8321
8507
  process.exitCode = 1;
8322
8508
  return;
8323
8509
  }
8324
8510
  await writeFile19(file, serializeMemory17({ frontmatter, body }), "utf8");
8325
- ui.success(`Recorded: ${path23.relative(root, file)}`);
8511
+ ui.success(`Recorded: ${path24.relative(root, file)}`);
8326
8512
  ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
8327
8513
  });
8328
8514
  }
@@ -8333,18 +8519,18 @@ function parseCsv4(value) {
8333
8519
 
8334
8520
  // src/commands/memory-pending.ts
8335
8521
  import { existsSync as existsSync41 } from "fs";
8336
- import path24 from "path";
8522
+ import path25 from "path";
8337
8523
  import "commander";
8338
8524
  import {
8339
- findProjectRoot as findProjectRoot21,
8525
+ findProjectRoot as findProjectRoot22,
8340
8526
  getUsage as getUsage14,
8341
8527
  loadUsageIndex as loadUsageIndex16,
8342
- resolveHaivePaths as resolveHaivePaths18
8528
+ resolveHaivePaths as resolveHaivePaths19
8343
8529
  } from "@hiveai/core";
8344
8530
  function registerMemoryPending(memory2) {
8345
8531
  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);
8532
+ const root = findProjectRoot22(opts.dir);
8533
+ const paths = resolveHaivePaths19(root);
8348
8534
  if (!existsSync41(paths.memoriesDir)) {
8349
8535
  ui.error(`No .ai/memories at ${root}.`);
8350
8536
  process.exitCode = 1;
@@ -8373,7 +8559,7 @@ function registerMemoryPending(memory2) {
8373
8559
  console.log(
8374
8560
  `${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count} rejections=${u.rejected_count}`)}`
8375
8561
  );
8376
- console.log(` ${ui.dim(path24.relative(root, filePath))}`);
8562
+ console.log(` ${ui.dim(path25.relative(root, filePath))}`);
8377
8563
  }
8378
8564
  ui.info(`${proposed.length} pending`);
8379
8565
  });
@@ -8381,22 +8567,22 @@ function registerMemoryPending(memory2) {
8381
8567
 
8382
8568
  // src/commands/memory-query.ts
8383
8569
  import { existsSync as existsSync43 } from "fs";
8384
- import path25 from "path";
8570
+ import path26 from "path";
8385
8571
  import "commander";
8386
8572
  import {
8387
8573
  extractSnippet as extractSnippet2,
8388
- findProjectRoot as findProjectRoot22,
8574
+ findProjectRoot as findProjectRoot23,
8389
8575
  literalMatchesAllTokens as literalMatchesAllTokens3,
8390
8576
  literalMatchesAnyToken as literalMatchesAnyToken4,
8391
8577
  pickSnippetNeedle as pickSnippetNeedle2,
8392
- resolveHaivePaths as resolveHaivePaths19,
8578
+ resolveHaivePaths as resolveHaivePaths20,
8393
8579
  tokenizeQuery as tokenizeQuery6,
8394
8580
  trackReads as trackReads4
8395
8581
  } from "@hiveai/core";
8396
8582
  function registerMemoryQuery(memory2) {
8397
8583
  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);
8584
+ const root = findProjectRoot23(opts.dir);
8585
+ const paths = resolveHaivePaths20(root);
8400
8586
  if (!existsSync43(paths.memoriesDir)) {
8401
8587
  ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
8402
8588
  process.exitCode = 1;
@@ -8438,7 +8624,7 @@ function registerMemoryQuery(memory2) {
8438
8624
  const fm = mem.frontmatter;
8439
8625
  const statusBadge = ui.statusBadge(fm.status);
8440
8626
  console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
8441
- console.log(` ${ui.dim(path25.relative(root, filePath))}`);
8627
+ console.log(` ${ui.dim(path26.relative(root, filePath))}`);
8442
8628
  const snippet = extractSnippet2(mem.body, snippetNeedle);
8443
8629
  if (snippet) console.log(` ${snippet}`);
8444
8630
  }
@@ -8459,17 +8645,17 @@ import { writeFile as writeFile20 } from "fs/promises";
8459
8645
  import { existsSync as existsSync44 } from "fs";
8460
8646
  import "commander";
8461
8647
  import {
8462
- findProjectRoot as findProjectRoot23,
8648
+ findProjectRoot as findProjectRoot24,
8463
8649
  loadUsageIndex as loadUsageIndex17,
8464
8650
  recordRejection as recordRejection2,
8465
- resolveHaivePaths as resolveHaivePaths20,
8651
+ resolveHaivePaths as resolveHaivePaths21,
8466
8652
  saveUsageIndex as saveUsageIndex3,
8467
8653
  serializeMemory as serializeMemory18
8468
8654
  } from "@hiveai/core";
8469
8655
  function registerMemoryReject(memory2) {
8470
8656
  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);
8657
+ const root = findProjectRoot24(opts.dir);
8658
+ const paths = resolveHaivePaths21(root);
8473
8659
  if (!existsSync44(paths.memoriesDir)) {
8474
8660
  ui.error(`No .ai/memories at ${root}.`);
8475
8661
  process.exitCode = 1;
@@ -8508,19 +8694,19 @@ function registerMemoryReject(memory2) {
8508
8694
  // src/commands/memory-rm.ts
8509
8695
  import { existsSync as existsSync45 } from "fs";
8510
8696
  import { unlink as unlink3 } from "fs/promises";
8511
- import path26 from "path";
8512
- import { createInterface } from "readline/promises";
8697
+ import path27 from "path";
8698
+ import { createInterface as createInterface2 } from "readline/promises";
8513
8699
  import "commander";
8514
8700
  import {
8515
- findProjectRoot as findProjectRoot24,
8701
+ findProjectRoot as findProjectRoot25,
8516
8702
  loadUsageIndex as loadUsageIndex18,
8517
- resolveHaivePaths as resolveHaivePaths21,
8703
+ resolveHaivePaths as resolveHaivePaths22,
8518
8704
  saveUsageIndex as saveUsageIndex4
8519
8705
  } from "@hiveai/core";
8520
8706
  function registerMemoryRm(memory2) {
8521
8707
  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);
8708
+ const root = findProjectRoot25(opts.dir);
8709
+ const paths = resolveHaivePaths22(root);
8524
8710
  if (!existsSync45(paths.memoriesDir)) {
8525
8711
  ui.error(`No .ai/memories at ${root}.`);
8526
8712
  process.exitCode = 1;
@@ -8533,9 +8719,9 @@ function registerMemoryRm(memory2) {
8533
8719
  process.exitCode = 1;
8534
8720
  return;
8535
8721
  }
8536
- const rel = path26.relative(root, found.filePath);
8722
+ const rel = path27.relative(root, found.filePath);
8537
8723
  if (!opts.yes) {
8538
- const rl = createInterface({ input: process.stdin, output: process.stdout });
8724
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
8539
8725
  const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
8540
8726
  rl.close();
8541
8727
  if (answer !== "y" && answer !== "yes") {
@@ -8559,19 +8745,19 @@ function registerMemoryRm(memory2) {
8559
8745
  // src/commands/memory-show.ts
8560
8746
  import { existsSync as existsSync46 } from "fs";
8561
8747
  import { readFile as readFile11 } from "fs/promises";
8562
- import path27 from "path";
8748
+ import path28 from "path";
8563
8749
  import "commander";
8564
8750
  import {
8565
8751
  deriveConfidence as deriveConfidence10,
8566
- findProjectRoot as findProjectRoot25,
8752
+ findProjectRoot as findProjectRoot26,
8567
8753
  getUsage as getUsage15,
8568
8754
  loadUsageIndex as loadUsageIndex19,
8569
- resolveHaivePaths as resolveHaivePaths22
8755
+ resolveHaivePaths as resolveHaivePaths23
8570
8756
  } from "@hiveai/core";
8571
8757
  function registerMemoryShow(memory2) {
8572
8758
  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);
8759
+ const root = findProjectRoot26(opts.dir);
8760
+ const paths = resolveHaivePaths23(root);
8575
8761
  if (!existsSync46(paths.memoriesDir)) {
8576
8762
  ui.error(`No .ai/memories at ${root}.`);
8577
8763
  process.exitCode = 1;
@@ -8601,7 +8787,7 @@ function registerMemoryShow(memory2) {
8601
8787
  if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
8602
8788
  if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
8603
8789
  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)}`);
8790
+ console.log(`${ui.dim("file:")} ${path28.relative(root, found.filePath)}`);
8605
8791
  if (fm.anchor.paths.length || fm.anchor.symbols.length) {
8606
8792
  console.log(ui.dim("anchor:"));
8607
8793
  if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
@@ -8617,19 +8803,19 @@ function registerMemoryShow(memory2) {
8617
8803
 
8618
8804
  // src/commands/memory-stats.ts
8619
8805
  import { existsSync as existsSync47 } from "fs";
8620
- import path28 from "path";
8806
+ import path29 from "path";
8621
8807
  import "commander";
8622
8808
  import {
8623
8809
  deriveConfidence as deriveConfidence11,
8624
- findProjectRoot as findProjectRoot26,
8810
+ findProjectRoot as findProjectRoot27,
8625
8811
  getUsage as getUsage16,
8626
8812
  loadUsageIndex as loadUsageIndex20,
8627
- resolveHaivePaths as resolveHaivePaths23
8813
+ resolveHaivePaths as resolveHaivePaths24
8628
8814
  } from "@hiveai/core";
8629
8815
  function registerMemoryStats(memory2) {
8630
8816
  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);
8817
+ const root = findProjectRoot27(opts.dir);
8818
+ const paths = resolveHaivePaths24(root);
8633
8819
  if (!existsSync47(paths.memoriesDir)) {
8634
8820
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8635
8821
  process.exitCode = 1;
@@ -8655,7 +8841,7 @@ function registerMemoryStats(memory2) {
8655
8841
  console.log(
8656
8842
  ` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
8657
8843
  );
8658
- console.log(` ${ui.dim(path28.relative(root, filePath))}`);
8844
+ console.log(` ${ui.dim(path29.relative(root, filePath))}`);
8659
8845
  }
8660
8846
  });
8661
8847
  }
@@ -8663,11 +8849,11 @@ function registerMemoryStats(memory2) {
8663
8849
  // src/commands/memory-verify.ts
8664
8850
  import { writeFile as writeFile21 } from "fs/promises";
8665
8851
  import { existsSync as existsSync48 } from "fs";
8666
- import path29 from "path";
8852
+ import path30 from "path";
8667
8853
  import "commander";
8668
8854
  import {
8669
- findProjectRoot as findProjectRoot27,
8670
- resolveHaivePaths as resolveHaivePaths24,
8855
+ findProjectRoot as findProjectRoot28,
8856
+ resolveHaivePaths as resolveHaivePaths25,
8671
8857
  serializeMemory as serializeMemory19,
8672
8858
  verifyAnchor as verifyAnchor3
8673
8859
  } from "@hiveai/core";
@@ -8675,8 +8861,8 @@ function registerMemoryVerify(memory2) {
8675
8861
  memory2.command("verify").description(
8676
8862
  "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
8863
  ).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);
8864
+ const root = findProjectRoot28(opts.dir);
8865
+ const paths = resolveHaivePaths25(root);
8680
8866
  if (!existsSync48(paths.memoriesDir)) {
8681
8867
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
8682
8868
  process.exitCode = 1;
@@ -8700,7 +8886,7 @@ function registerMemoryVerify(memory2) {
8700
8886
  anchorlessIds.push(mem.frontmatter.id);
8701
8887
  continue;
8702
8888
  }
8703
- const rel = path29.relative(root, filePath);
8889
+ const rel = path30.relative(root, filePath);
8704
8890
  if (result.stale) {
8705
8891
  staleCount++;
8706
8892
  console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
@@ -8767,15 +8953,15 @@ import { readFile as readFile12 } from "fs/promises";
8767
8953
  import { existsSync as existsSync49 } from "fs";
8768
8954
  import "commander";
8769
8955
  import {
8770
- findProjectRoot as findProjectRoot28,
8771
- resolveHaivePaths as resolveHaivePaths25
8956
+ findProjectRoot as findProjectRoot29,
8957
+ resolveHaivePaths as resolveHaivePaths26
8772
8958
  } from "@hiveai/core";
8773
8959
  function registerMemoryImport(memory2) {
8774
8960
  memory2.command("import").description(
8775
8961
  "Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
8776
8962
  ).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);
8963
+ const root = findProjectRoot29(opts.dir);
8964
+ const paths = resolveHaivePaths26(root);
8779
8965
  if (!existsSync49(paths.haiveDir)) {
8780
8966
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
8781
8967
  process.exitCode = 1;
@@ -8815,13 +9001,13 @@ function registerMemoryImport(memory2) {
8815
9001
 
8816
9002
  // src/commands/memory-import-changelog.ts
8817
9003
  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";
9004
+ import { readFile as readFile13, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
9005
+ import path31 from "path";
8820
9006
  import "commander";
8821
9007
  import {
8822
9008
  buildFrontmatter as buildFrontmatter9,
8823
- findProjectRoot as findProjectRoot29,
8824
- resolveHaivePaths as resolveHaivePaths26,
9009
+ findProjectRoot as findProjectRoot30,
9010
+ resolveHaivePaths as resolveHaivePaths27,
8825
9011
  serializeMemory as serializeMemory20
8826
9012
  } from "@hiveai/core";
8827
9013
  function parseChangelog(content) {
@@ -8886,9 +9072,9 @@ function registerMemoryImportChangelog(memory2) {
8886
9072
  "--versions <csv>",
8887
9073
  "only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
8888
9074
  ).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);
9075
+ const root = findProjectRoot30(opts.dir);
9076
+ const paths = resolveHaivePaths27(root);
9077
+ const changelogPath = path31.resolve(root, opts.fromChangelog);
8892
9078
  if (!existsSync50(changelogPath)) {
8893
9079
  ui.error(`CHANGELOG not found: ${changelogPath}`);
8894
9080
  process.exitCode = 1;
@@ -8909,10 +9095,10 @@ function registerMemoryImportChangelog(memory2) {
8909
9095
  entries = entries.filter((e) => requested.includes(e.version));
8910
9096
  }
8911
9097
  }
8912
- const pkgName = opts.package ?? path30.basename(path30.dirname(changelogPath));
9098
+ const pkgName = opts.package ?? path31.basename(path31.dirname(changelogPath));
8913
9099
  const scope = opts.scope ?? "team";
8914
- const teamDir = path30.join(paths.memoriesDir, scope);
8915
- await mkdir13(teamDir, { recursive: true });
9100
+ const teamDir = path31.join(paths.memoriesDir, scope);
9101
+ await mkdir14(teamDir, { recursive: true });
8916
9102
  let saved = 0;
8917
9103
  for (const entry of entries) {
8918
9104
  const lines = [];
@@ -8934,7 +9120,7 @@ function registerMemoryImportChangelog(memory2) {
8934
9120
  lines.push("");
8935
9121
  }
8936
9122
  lines.push(
8937
- `**Source:** \`${path30.relative(root, changelogPath)}\`
9123
+ `**Source:** \`${path31.relative(root, changelogPath)}\`
8938
9124
  **Action:** Update all usages of ${pkgName} if they rely on any of the above.`
8939
9125
  );
8940
9126
  const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
@@ -8949,11 +9135,11 @@ function registerMemoryImportChangelog(memory2) {
8949
9135
  pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
8950
9136
  `v${entry.version}`
8951
9137
  ],
8952
- paths: [path30.relative(root, changelogPath)],
9138
+ paths: [path31.relative(root, changelogPath)],
8953
9139
  topic: `changelog-${pkgName}-${entry.version}`
8954
9140
  });
8955
9141
  await writeFile23(
8956
- path30.join(teamDir, `${fm.id}.md`),
9142
+ path31.join(teamDir, `${fm.id}.md`),
8957
9143
  serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
8958
9144
  "utf8"
8959
9145
  );
@@ -8978,15 +9164,15 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
8978
9164
  // src/commands/memory-digest.ts
8979
9165
  import { existsSync as existsSync51 } from "fs";
8980
9166
  import { writeFile as writeFile24 } from "fs/promises";
8981
- import path31 from "path";
9167
+ import path33 from "path";
8982
9168
  import "commander";
8983
9169
  import {
8984
9170
  deriveConfidence as deriveConfidence12,
8985
- findProjectRoot as findProjectRoot30,
9171
+ findProjectRoot as findProjectRoot31,
8986
9172
  getUsage as getUsage17,
8987
9173
  loadMemoriesFromDir as loadMemoriesFromDir26,
8988
9174
  loadUsageIndex as loadUsageIndex21,
8989
- resolveHaivePaths as resolveHaivePaths27
9175
+ resolveHaivePaths as resolveHaivePaths28
8990
9176
  } from "@hiveai/core";
8991
9177
  var CONFIDENCE_EMOJI = {
8992
9178
  unverified: "\u2B1C",
@@ -8999,8 +9185,8 @@ function registerMemoryDigest(program2) {
8999
9185
  program2.command("digest").description(
9000
9186
  "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
9187
  ).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);
9188
+ const root = findProjectRoot31(opts.dir);
9189
+ const paths = resolveHaivePaths28(root);
9004
9190
  if (!existsSync51(paths.memoriesDir)) {
9005
9191
  ui.error("No .ai/memories found. Run `haive init` first.");
9006
9192
  process.exitCode = 1;
@@ -9073,7 +9259,7 @@ function registerMemoryDigest(program2) {
9073
9259
  );
9074
9260
  const digest = lines.join("\n");
9075
9261
  if (opts.out) {
9076
- const outPath = path31.resolve(process.cwd(), opts.out);
9262
+ const outPath = path33.resolve(process.cwd(), opts.out);
9077
9263
  await writeFile24(outPath, digest, "utf8");
9078
9264
  ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
9079
9265
  } else {
@@ -9083,20 +9269,20 @@ function registerMemoryDigest(program2) {
9083
9269
  }
9084
9270
 
9085
9271
  // src/commands/session-end.ts
9086
- import { writeFile as writeFile25, mkdir as mkdir14, readFile as readFile14, rm as rm2 } from "fs/promises";
9272
+ import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile14, rm as rm2 } from "fs/promises";
9087
9273
  import { existsSync as existsSync53 } from "fs";
9088
- import path33 from "path";
9274
+ import path34 from "path";
9089
9275
  import "commander";
9090
9276
  import {
9091
9277
  buildFrontmatter as buildFrontmatter10,
9092
- findProjectRoot as findProjectRoot31,
9278
+ findProjectRoot as findProjectRoot32,
9093
9279
  loadMemoriesFromDir as loadMemoriesFromDir27,
9094
9280
  memoryFilePath as memoryFilePath9,
9095
- resolveHaivePaths as resolveHaivePaths28,
9281
+ resolveHaivePaths as resolveHaivePaths29,
9096
9282
  serializeMemory as serializeMemory21
9097
9283
  } from "@hiveai/core";
9098
9284
  async function buildAutoRecap(paths) {
9099
- const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
9285
+ const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
9100
9286
  if (!existsSync53(obsFile)) return null;
9101
9287
  const raw = await readFile14(obsFile, "utf8").catch(() => "");
9102
9288
  if (!raw.trim()) return null;
@@ -9176,8 +9362,8 @@ function registerSessionEnd(session2) {
9176
9362
  --next "Add integration tests for webhook signature validation"
9177
9363
  `
9178
9364
  ).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);
9365
+ const root = findProjectRoot32(opts.dir);
9366
+ const paths = resolveHaivePaths29(root);
9181
9367
  if (!existsSync53(paths.haiveDir)) {
9182
9368
  if (opts.auto || opts.quiet) return;
9183
9369
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
@@ -9210,14 +9396,14 @@ function registerSessionEnd(session2) {
9210
9396
  });
9211
9397
  const topic = recapTopic2(scope, opts.module);
9212
9398
  const filesTouched = parseCsv5(resolvedFiles);
9213
- const missingPaths = filesTouched.filter((p) => !existsSync53(path33.resolve(root, p)));
9399
+ const missingPaths = filesTouched.filter((p) => !existsSync53(path34.resolve(root, p)));
9214
9400
  if (missingPaths.length > 0 && !opts.quiet) {
9215
9401
  ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
9216
9402
  for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
9217
9403
  }
9218
9404
  const cleanupObservations = async () => {
9219
9405
  if (!opts.auto) return;
9220
- const obsFile = path33.join(paths.haiveDir, ".cache", "observations.jsonl");
9406
+ const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
9221
9407
  if (existsSync53(obsFile)) await rm2(obsFile).catch(() => {
9222
9408
  });
9223
9409
  };
@@ -9241,7 +9427,7 @@ function registerSessionEnd(session2) {
9241
9427
  await cleanupObservations();
9242
9428
  if (!opts.quiet) {
9243
9429
  ui.success(`Session recap updated (revision #${revisionCount})`);
9244
- ui.info(`id=${fm.id} file=${path33.relative(root, topicMatch.filePath)}`);
9430
+ ui.info(`id=${fm.id} file=${path34.relative(root, topicMatch.filePath)}`);
9245
9431
  ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
9246
9432
  }
9247
9433
  return;
@@ -9258,12 +9444,12 @@ function registerSessionEnd(session2) {
9258
9444
  status: "validated"
9259
9445
  });
9260
9446
  const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
9261
- await mkdir14(path33.dirname(file), { recursive: true });
9447
+ await mkdir15(path34.dirname(file), { recursive: true });
9262
9448
  await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
9263
9449
  await cleanupObservations();
9264
9450
  if (!opts.quiet) {
9265
9451
  ui.success(`Session recap created`);
9266
- ui.info(`id=${frontmatter.id} scope=${scope} file=${path33.relative(root, file)}`);
9452
+ ui.info(`id=${frontmatter.id} scope=${scope} file=${path34.relative(root, file)}`);
9267
9453
  ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
9268
9454
  ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
9269
9455
  }
@@ -9277,13 +9463,13 @@ function parseCsv5(value) {
9277
9463
  // src/commands/snapshot.ts
9278
9464
  import { existsSync as existsSync54 } from "fs";
9279
9465
  import { readdir as readdir4 } from "fs/promises";
9280
- import path34 from "path";
9466
+ import path35 from "path";
9281
9467
  import "commander";
9282
9468
  import {
9283
9469
  diffContract,
9284
- findProjectRoot as findProjectRoot32,
9470
+ findProjectRoot as findProjectRoot33,
9285
9471
  loadConfig as loadConfig5,
9286
- resolveHaivePaths as resolveHaivePaths29,
9472
+ resolveHaivePaths as resolveHaivePaths30,
9287
9473
  snapshotContract
9288
9474
  } from "@hiveai/core";
9289
9475
  function registerSnapshot(program2) {
@@ -9308,15 +9494,15 @@ function registerSnapshot(program2) {
9308
9494
  "--format <format>",
9309
9495
  "contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
9310
9496
  ).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);
9497
+ const root = findProjectRoot33(opts.dir);
9498
+ const paths = resolveHaivePaths30(root);
9313
9499
  if (!existsSync54(paths.haiveDir)) {
9314
9500
  ui.error("No .ai/ found. Run `haive init` first.");
9315
9501
  process.exitCode = 1;
9316
9502
  return;
9317
9503
  }
9318
9504
  if (opts.list) {
9319
- const contractsDir = path34.join(paths.haiveDir, "contracts");
9505
+ const contractsDir = path35.join(paths.haiveDir, "contracts");
9320
9506
  if (!existsSync54(contractsDir)) {
9321
9507
  console.log(ui.dim("No contract snapshots found."));
9322
9508
  return;
@@ -9372,7 +9558,7 @@ function registerSnapshot(program2) {
9372
9558
  return;
9373
9559
  }
9374
9560
  const contractPath = opts.contract;
9375
- const name = opts.name ?? path34.basename(contractPath, path34.extname(contractPath));
9561
+ const name = opts.name ?? path35.basename(contractPath, path35.extname(contractPath));
9376
9562
  const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
9377
9563
  const contract = { name, path: contractPath, format };
9378
9564
  try {
@@ -9427,8 +9613,8 @@ async function runDiff(root, haiveDir, contract) {
9427
9613
  }
9428
9614
  }
9429
9615
  function detectFormat(filePath) {
9430
- const ext = path34.extname(filePath).toLowerCase();
9431
- const base = path34.basename(filePath).toLowerCase();
9616
+ const ext = path35.extname(filePath).toLowerCase();
9617
+ const base = path35.basename(filePath).toLowerCase();
9432
9618
  if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
9433
9619
  if (base.includes("openapi") || base.includes("swagger")) return "openapi";
9434
9620
  if (base.includes("schema") || base.includes("graphql")) return "graphql";
@@ -9442,15 +9628,15 @@ function detectFormat(filePath) {
9442
9628
 
9443
9629
  // src/commands/hub.ts
9444
9630
  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";
9631
+ import { mkdir as mkdir16, readFile as readFile15, writeFile as writeFile26, copyFile } from "fs/promises";
9632
+ import path36 from "path";
9633
+ import { spawnSync as spawnSync4 } from "child_process";
9448
9634
  import "commander";
9449
9635
  import {
9450
- findProjectRoot as findProjectRoot33,
9636
+ findProjectRoot as findProjectRoot34,
9451
9637
  loadConfig as loadConfig6,
9452
9638
  loadMemoriesFromDir as loadMemoriesFromDir28,
9453
- resolveHaivePaths as resolveHaivePaths30,
9639
+ resolveHaivePaths as resolveHaivePaths31,
9454
9640
  saveConfig as saveConfig2,
9455
9641
  serializeMemory as serializeMemory23
9456
9642
  } from "@hiveai/core";
@@ -9462,21 +9648,21 @@ function registerHub(program2) {
9462
9648
  hub.command("init <hubPath>").description(
9463
9649
  "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
9650
  ).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 });
9651
+ const absPath = path36.resolve(hubPath);
9652
+ await mkdir16(absPath, { recursive: true });
9653
+ const gitCheck = spawnSync4("git", ["rev-parse", "--git-dir"], { cwd: absPath });
9468
9654
  if (gitCheck.status !== 0) {
9469
- const init = spawnSync3("git", ["init"], { cwd: absPath, encoding: "utf8" });
9655
+ const init = spawnSync4("git", ["init"], { cwd: absPath, encoding: "utf8" });
9470
9656
  if (init.status !== 0) {
9471
9657
  ui.error(`git init failed: ${init.stderr}`);
9472
9658
  process.exitCode = 1;
9473
9659
  return;
9474
9660
  }
9475
9661
  }
9476
- const sharedDir = path35.join(absPath, ".ai", "memories", "shared");
9477
- await mkdir15(sharedDir, { recursive: true });
9662
+ const sharedDir = path36.join(absPath, ".ai", "memories", "shared");
9663
+ await mkdir16(sharedDir, { recursive: true });
9478
9664
  await writeFile26(
9479
- path35.join(absPath, ".ai", "README.md"),
9665
+ path36.join(absPath, ".ai", "README.md"),
9480
9666
  `# hAIve Team Knowledge Hub
9481
9667
 
9482
9668
  This repo is a shared knowledge hub for hAIve.
@@ -9498,12 +9684,12 @@ haive hub pull # import into a project
9498
9684
  "utf8"
9499
9685
  );
9500
9686
  await writeFile26(
9501
- path35.join(absPath, ".gitignore"),
9687
+ path36.join(absPath, ".gitignore"),
9502
9688
  ".ai/.cache/\n.ai/memories/personal/\n",
9503
9689
  "utf8"
9504
9690
  );
9505
- spawnSync3("git", ["add", "."], { cwd: absPath });
9506
- spawnSync3("git", ["commit", "-m", "chore: initialize hAIve team-knowledge hub"], {
9691
+ spawnSync4("git", ["add", "."], { cwd: absPath });
9692
+ spawnSync4("git", ["commit", "-m", "chore: initialize hAIve team-knowledge hub"], {
9507
9693
  cwd: absPath,
9508
9694
  encoding: "utf8"
9509
9695
  });
@@ -9513,7 +9699,7 @@ haive hub pull # import into a project
9513
9699
  `
9514
9700
  Next steps:
9515
9701
  1. Add hubPath to your project's .ai/haive.config.json:
9516
- { "hubPath": "${path35.relative(process.cwd(), absPath)}" }
9702
+ { "hubPath": "${path36.relative(process.cwd(), absPath)}" }
9517
9703
  2. Run \`haive hub push\` to publish your shared memories
9518
9704
  3. Share ${absPath} with teammates (git remote, NFS, etc.)
9519
9705
  `
@@ -9532,8 +9718,8 @@ Next steps:
9532
9718
  haive hub push --commit --message "feat: add payment API contract memories"
9533
9719
  `
9534
9720
  ).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);
9721
+ const root = findProjectRoot34(opts.dir);
9722
+ const paths = resolveHaivePaths31(root);
9537
9723
  const config = await loadConfig6(paths);
9538
9724
  if (!config.hubPath) {
9539
9725
  ui.error(
@@ -9542,15 +9728,15 @@ Next steps:
9542
9728
  process.exitCode = 1;
9543
9729
  return;
9544
9730
  }
9545
- const hubRoot = path35.resolve(root, config.hubPath);
9731
+ const hubRoot = path36.resolve(root, config.hubPath);
9546
9732
  if (!existsSync55(hubRoot)) {
9547
9733
  ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
9548
9734
  process.exitCode = 1;
9549
9735
  return;
9550
9736
  }
9551
- const projectName = path35.basename(root);
9552
- const destDir = path35.join(hubRoot, ".ai", "memories", "shared", projectName);
9553
- await mkdir15(destDir, { recursive: true });
9737
+ const projectName = path36.basename(root);
9738
+ const destDir = path36.join(hubRoot, ".ai", "memories", "shared", projectName);
9739
+ await mkdir16(destDir, { recursive: true });
9554
9740
  const all = await loadMemoriesFromDir28(paths.memoriesDir);
9555
9741
  const shared = all.filter(
9556
9742
  ({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
@@ -9568,7 +9754,7 @@ Next steps:
9568
9754
  for (const { memory: memory2 } of shared) {
9569
9755
  const fm = memory2.frontmatter;
9570
9756
  const fileName = `${fm.id}.md`;
9571
- const destPath = path35.join(destDir, fileName);
9757
+ const destPath = path36.join(destDir, fileName);
9572
9758
  await writeFile26(destPath, serializeMemory23(memory2), "utf8");
9573
9759
  pushed++;
9574
9760
  }
@@ -9576,10 +9762,10 @@ Next steps:
9576
9762
  console.log(ui.dim(` Location: ${destDir}`));
9577
9763
  if (opts.commit) {
9578
9764
  const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
9579
- spawnSync3("git", ["add", path35.join(".ai", "memories", "shared", projectName)], {
9765
+ spawnSync4("git", ["add", path36.join(".ai", "memories", "shared", projectName)], {
9580
9766
  cwd: hubRoot
9581
9767
  });
9582
- const commit = spawnSync3("git", ["commit", "-m", message], {
9768
+ const commit = spawnSync4("git", ["commit", "-m", message], {
9583
9769
  cwd: hubRoot,
9584
9770
  encoding: "utf8"
9585
9771
  });
@@ -9601,8 +9787,8 @@ Next steps:
9601
9787
  hub.command("pull").description(
9602
9788
  "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
9789
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
9604
- const root = findProjectRoot33(opts.dir);
9605
- const paths = resolveHaivePaths30(root);
9790
+ const root = findProjectRoot34(opts.dir);
9791
+ const paths = resolveHaivePaths31(root);
9606
9792
  const config = await loadConfig6(paths);
9607
9793
  if (!config.hubPath) {
9608
9794
  ui.error(
@@ -9611,13 +9797,13 @@ Next steps:
9611
9797
  process.exitCode = 1;
9612
9798
  return;
9613
9799
  }
9614
- const hubRoot = path35.resolve(root, config.hubPath);
9615
- const hubSharedDir = path35.join(hubRoot, ".ai", "memories", "shared");
9800
+ const hubRoot = path36.resolve(root, config.hubPath);
9801
+ const hubSharedDir = path36.join(hubRoot, ".ai", "memories", "shared");
9616
9802
  if (!existsSync55(hubSharedDir)) {
9617
9803
  ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
9618
9804
  return;
9619
9805
  }
9620
- const projectName = path35.basename(root);
9806
+ const projectName = path36.basename(root);
9621
9807
  const { readdir: readdir6 } = await import("fs/promises");
9622
9808
  const projectDirs = (await readdir6(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
9623
9809
  if (projectDirs.length === 0) {
@@ -9627,16 +9813,16 @@ Next steps:
9627
9813
  let totalImported = 0;
9628
9814
  let totalUpdated = 0;
9629
9815
  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 });
9816
+ const sourceDir = path36.join(hubSharedDir, sourceName);
9817
+ const destDir = path36.join(paths.memoriesDir, "shared", sourceName);
9818
+ await mkdir16(destDir, { recursive: true });
9633
9819
  const sourceFiles = (await readdir6(sourceDir)).filter((f) => f.endsWith(".md"));
9634
9820
  const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
9635
9821
  const existingInDest = await loadDir(destDir);
9636
9822
  const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
9637
9823
  for (const file of sourceFiles) {
9638
- const srcPath = path35.join(sourceDir, file);
9639
- const destPath = path35.join(destDir, file);
9824
+ const srcPath = path36.join(sourceDir, file);
9825
+ const destPath = path36.join(destDir, file);
9640
9826
  const fileContent = await readFile15(srcPath, "utf8");
9641
9827
  const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
9642
9828
  if (!alreadyTagged) {
@@ -9660,21 +9846,21 @@ Next steps:
9660
9846
  );
9661
9847
  });
9662
9848
  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);
9849
+ const root = findProjectRoot34(opts.dir);
9850
+ const paths = resolveHaivePaths31(root);
9665
9851
  const config = await loadConfig6(paths);
9666
9852
  console.log(ui.bold("Hub status"));
9667
9853
  console.log(
9668
9854
  ` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
9669
9855
  );
9670
- const sharedDir = path35.join(paths.memoriesDir, "shared");
9856
+ const sharedDir = path36.join(paths.memoriesDir, "shared");
9671
9857
  if (existsSync55(sharedDir)) {
9672
9858
  const { readdir: readdir6 } = await import("fs/promises");
9673
9859
  const sources = (await readdir6(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
9674
9860
  console.log(`
9675
9861
  Imported from ${sources.length} source(s):`);
9676
9862
  for (const src of sources) {
9677
- const files = (await readdir6(path35.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
9863
+ const files = (await readdir6(path36.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
9678
9864
  console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
9679
9865
  }
9680
9866
  } else {
@@ -9698,16 +9884,16 @@ Next steps:
9698
9884
  // src/commands/stats.ts
9699
9885
  import "commander";
9700
9886
  import { existsSync as existsSync56 } from "fs";
9701
- import { mkdir as mkdir16, writeFile as writeFile27 } from "fs/promises";
9702
- import path36 from "path";
9887
+ import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
9888
+ import path37 from "path";
9703
9889
  import {
9704
9890
  aggregateUsage,
9705
- findProjectRoot as findProjectRoot34,
9891
+ findProjectRoot as findProjectRoot35,
9706
9892
  loadMemoriesFromDir as loadMemoriesFromDir29,
9707
9893
  loadUsageIndex as loadUsageIndex23,
9708
9894
  parseSince,
9709
9895
  readUsageEvents as readUsageEvents2,
9710
- resolveHaivePaths as resolveHaivePaths31,
9896
+ resolveHaivePaths as resolveHaivePaths32,
9711
9897
  usageLogSize
9712
9898
  } from "@hiveai/core";
9713
9899
  function registerStats(program2) {
@@ -9716,8 +9902,8 @@ function registerStats(program2) {
9716
9902
  "write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
9717
9903
  void 0
9718
9904
  ).option("-d, --dir <dir>", "project root").action(async (opts) => {
9719
- const root = findProjectRoot34(opts.dir);
9720
- const paths = resolveHaivePaths31(root);
9905
+ const root = findProjectRoot35(opts.dir);
9906
+ const paths = resolveHaivePaths32(root);
9721
9907
  if (opts.exportReport) {
9722
9908
  await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
9723
9909
  return;
@@ -9771,7 +9957,7 @@ function registerStats(program2) {
9771
9957
  });
9772
9958
  }
9773
9959
  async function writeRoiReport(paths, root, sinceRaw, outRelative) {
9774
- const outAbs = path36.isAbsolute(outRelative) ? path36.resolve(outRelative) : path36.resolve(root, outRelative);
9960
+ const outAbs = path37.isAbsolute(outRelative) ? path37.resolve(outRelative) : path37.resolve(root, outRelative);
9775
9961
  const size = await usageLogSize(paths);
9776
9962
  let events = await readUsageEvents2(paths);
9777
9963
  let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
@@ -9806,7 +9992,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
9806
9992
  ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
9807
9993
  events = [];
9808
9994
  }
9809
- await mkdir16(path36.dirname(outAbs), { recursive: true });
9995
+ await mkdir17(path37.dirname(outAbs), { recursive: true });
9810
9996
  const payload = {
9811
9997
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
9812
9998
  project_root: root,
@@ -9870,13 +10056,13 @@ import { performance } from "perf_hooks";
9870
10056
  import "commander";
9871
10057
  import {
9872
10058
  estimateTokens as estimateTokens3,
9873
- findProjectRoot as findProjectRoot35,
9874
- resolveHaivePaths as resolveHaivePaths32
10059
+ findProjectRoot as findProjectRoot36,
10060
+ resolveHaivePaths as resolveHaivePaths33
9875
10061
  } from "@hiveai/core";
9876
10062
  function registerBench(program2) {
9877
10063
  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);
10064
+ const root = findProjectRoot36(opts.dir);
10065
+ const paths = resolveHaivePaths33(root);
9880
10066
  const ctx = { paths };
9881
10067
  const task = opts.task ?? "audit dependencies for security risks";
9882
10068
  const scenarios = [
@@ -9997,9 +10183,9 @@ function summarize(name, t0, payload, notes) {
9997
10183
  // src/commands/benchmark.ts
9998
10184
  import { existsSync as existsSync57 } from "fs";
9999
10185
  import { readdir as readdir5, readFile as readFile16, writeFile as writeFile28 } from "fs/promises";
10000
- import path37 from "path";
10186
+ import path38 from "path";
10001
10187
  import "commander";
10002
- import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot36 } from "@hiveai/core";
10188
+ import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot37 } from "@hiveai/core";
10003
10189
  function registerBenchmark(program2) {
10004
10190
  const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
10005
10191
  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 +10198,9 @@ function registerBenchmark(program2) {
10012
10198
  }
10013
10199
  const markdown = renderMarkdown(root, summary, rows);
10014
10200
  if (opts.out) {
10015
- const outFile = path37.isAbsolute(opts.out) ? opts.out : path37.join(root, opts.out);
10201
+ const outFile = path38.isAbsolute(opts.out) ? opts.out : path38.join(root, opts.out);
10016
10202
  await writeFile28(outFile, markdown, "utf8");
10017
- ui.success(`wrote ${path37.relative(process.cwd(), outFile)}`);
10203
+ ui.success(`wrote ${path38.relative(process.cwd(), outFile)}`);
10018
10204
  return;
10019
10205
  }
10020
10206
  console.log(markdown);
@@ -10038,9 +10224,9 @@ function registerBenchmark(program2) {
10038
10224
  }
10039
10225
  function resolveBenchmarkRoot(dir) {
10040
10226
  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);
10227
+ if (path38.isAbsolute(candidate)) return candidate;
10228
+ const projectRoot = findProjectRoot37(process.cwd());
10229
+ return path38.join(projectRoot, candidate);
10044
10230
  }
10045
10231
  async function collectRows(root) {
10046
10232
  if (!existsSync57(root)) throw new Error(`Benchmark directory not found: ${root}`);
@@ -10048,8 +10234,8 @@ async function collectRows(root) {
10048
10234
  const rows = [];
10049
10235
  for (const entry of entries) {
10050
10236
  if (!entry.isDirectory()) continue;
10051
- const fixtureDir = path37.join(root, entry.name);
10052
- const reportFile = path37.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
10237
+ const fixtureDir = path38.join(root, entry.name);
10238
+ const reportFile = path38.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
10053
10239
  if (!existsSync57(reportFile)) continue;
10054
10240
  const report = await readFile16(reportFile, "utf8");
10055
10241
  rows.push(parseAgentReport(entry.name, report));
@@ -10140,19 +10326,19 @@ function escapeRegExp(value) {
10140
10326
  }
10141
10327
 
10142
10328
  // src/commands/memory-suggest.ts
10143
- import { mkdir as mkdir17, writeFile as writeFile29 } from "fs/promises";
10329
+ import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
10144
10330
  import { existsSync as existsSync58 } from "fs";
10145
- import path38 from "path";
10331
+ import path39 from "path";
10146
10332
  import "commander";
10147
10333
  import {
10148
10334
  aggregateUsage as aggregateUsage2,
10149
10335
  buildFrontmatter as buildFrontmatter11,
10150
- findProjectRoot as findProjectRoot37,
10336
+ findProjectRoot as findProjectRoot38,
10151
10337
  loadMemoriesFromDir as loadMemoriesFromDir30,
10152
10338
  memoryFilePath as memoryFilePath10,
10153
10339
  parseSince as parseSince2,
10154
10340
  readUsageEvents as readUsageEvents3,
10155
- resolveHaivePaths as resolveHaivePaths33,
10341
+ resolveHaivePaths as resolveHaivePaths34,
10156
10342
  serializeMemory as serializeMemory24
10157
10343
  } from "@hiveai/core";
10158
10344
  var SEARCH_TOOLS = /* @__PURE__ */ new Set([
@@ -10165,8 +10351,8 @@ function registerMemorySuggest(memory2) {
10165
10351
  memory2.command("suggest").description(
10166
10352
  "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
10353
  ).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);
10354
+ const root = findProjectRoot38(opts.dir);
10355
+ const paths = resolveHaivePaths34(root);
10170
10356
  const events = await readUsageEvents3(paths);
10171
10357
  if (events.length === 0) {
10172
10358
  if (opts.json) {
@@ -10235,13 +10421,13 @@ function registerMemorySuggest(memory2) {
10235
10421
  fm.status = "draft";
10236
10422
  const body = renderTemplate(s);
10237
10423
  const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
10238
- await mkdir17(path38.dirname(file), { recursive: true });
10424
+ await mkdir18(path39.dirname(file), { recursive: true });
10239
10425
  if (existsSync58(file)) {
10240
- skipped.push({ query: s.query, reason: `file already exists at ${path38.relative(root, file)}` });
10426
+ skipped.push({ query: s.query, reason: `file already exists at ${path39.relative(root, file)}` });
10241
10427
  continue;
10242
10428
  }
10243
10429
  await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
10244
- created.push({ id: fm.id, file: path38.relative(root, file), query: s.query });
10430
+ created.push({ id: fm.id, file: path39.relative(root, file), query: s.query });
10245
10431
  }
10246
10432
  if (opts.json) {
10247
10433
  console.log(JSON.stringify({ created, skipped }, null, 2));
@@ -10336,14 +10522,14 @@ function truncate2(text, max) {
10336
10522
  // src/commands/memory-archive.ts
10337
10523
  import { existsSync as existsSync59 } from "fs";
10338
10524
  import { writeFile as writeFile30 } from "fs/promises";
10339
- import path39 from "path";
10525
+ import path40 from "path";
10340
10526
  import "commander";
10341
10527
  import {
10342
- findProjectRoot as findProjectRoot38,
10528
+ findProjectRoot as findProjectRoot39,
10343
10529
  getUsage as getUsage18,
10344
10530
  loadMemoriesFromDir as loadMemoriesFromDir31,
10345
10531
  loadUsageIndex as loadUsageIndex24,
10346
- resolveHaivePaths as resolveHaivePaths34,
10532
+ resolveHaivePaths as resolveHaivePaths35,
10347
10533
  serializeMemory as serializeMemory25
10348
10534
  } from "@hiveai/core";
10349
10535
  var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
@@ -10351,8 +10537,8 @@ function registerMemoryArchive(memory2) {
10351
10537
  memory2.command("archive").description(
10352
10538
  "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
10539
  ).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);
10540
+ const root = findProjectRoot39(opts.dir);
10541
+ const paths = resolveHaivePaths35(root);
10356
10542
  if (!existsSync59(paths.memoriesDir)) {
10357
10543
  ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
10358
10544
  process.exitCode = 1;
@@ -10374,7 +10560,7 @@ function registerMemoryArchive(memory2) {
10374
10560
  if (typeFilter && fm.type !== typeFilter) continue;
10375
10561
  if (fm.status === "deprecated" || fm.status === "rejected") continue;
10376
10562
  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)));
10563
+ const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(path40.join(paths.root, p)));
10378
10564
  const isAnchorless = !hasAnyAnchor;
10379
10565
  if (!isAnchorless && !allPathsGone) continue;
10380
10566
  const u = getUsage18(usage, fm.id);
@@ -10450,27 +10636,27 @@ function parseDays(input) {
10450
10636
  // src/commands/doctor.ts
10451
10637
  import { existsSync as existsSync60 } from "fs";
10452
10638
  import { stat } from "fs/promises";
10453
- import path40 from "path";
10639
+ import path41 from "path";
10454
10640
  import { execSync as execSync3 } from "child_process";
10455
10641
  import "commander";
10456
10642
  import {
10457
10643
  codeMapPath as codeMapPath2,
10458
- findProjectRoot as findProjectRoot39,
10644
+ findProjectRoot as findProjectRoot40,
10459
10645
  getUsage as getUsage19,
10460
10646
  loadCodeMap as loadCodeMap5,
10461
10647
  loadConfig as loadConfig7,
10462
10648
  loadMemoriesFromDir as loadMemoriesFromDir32,
10463
10649
  loadUsageIndex as loadUsageIndex25,
10464
10650
  readUsageEvents as readUsageEvents4,
10465
- resolveHaivePaths as resolveHaivePaths35
10651
+ resolveHaivePaths as resolveHaivePaths36
10466
10652
  } from "@hiveai/core";
10467
10653
  var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
10468
10654
  function registerDoctor(program2) {
10469
10655
  program2.command("doctor").description(
10470
10656
  "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
10657
  ).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);
10658
+ const root = findProjectRoot40(opts.dir);
10659
+ const paths = resolveHaivePaths36(root);
10474
10660
  const findings = [];
10475
10661
  if (!existsSync60(paths.haiveDir)) {
10476
10662
  findings.push({
@@ -10612,7 +10798,7 @@ function registerDoctor(program2) {
10612
10798
  }
10613
10799
  const config = await loadConfig7(paths);
10614
10800
  if (config.enforcement?.requireBriefingFirst) {
10615
- const claudeSettings = path40.join(root, ".claude", "settings.local.json");
10801
+ const claudeSettings = path41.join(root, ".claude", "settings.local.json");
10616
10802
  let hasClaudeEnforcement = false;
10617
10803
  if (existsSync60(claudeSettings)) {
10618
10804
  try {
@@ -10646,7 +10832,7 @@ function registerDoctor(program2) {
10646
10832
  timeout: 3e3,
10647
10833
  stdio: ["ignore", "pipe", "ignore"]
10648
10834
  }).trim();
10649
- const cliVersion = "0.9.11";
10835
+ const cliVersion = "0.9.12";
10650
10836
  if (legacyRaw && legacyRaw !== cliVersion) {
10651
10837
  findings.push({
10652
10838
  severity: "warn",
@@ -10698,19 +10884,19 @@ function isSearchTool(name) {
10698
10884
  import { existsSync as existsSync61 } from "fs";
10699
10885
  import "commander";
10700
10886
  import {
10701
- findProjectRoot as findProjectRoot40,
10887
+ findProjectRoot as findProjectRoot41,
10702
10888
  loadMemoriesFromDir as loadMemoriesFromDir33,
10703
10889
  parseSince as parseSince3,
10704
10890
  readUsageEvents as readUsageEvents5,
10705
- resolveHaivePaths as resolveHaivePaths36
10891
+ resolveHaivePaths as resolveHaivePaths37
10706
10892
  } from "@hiveai/core";
10707
10893
  var MS_PER_MINUTE = 6e4;
10708
10894
  function registerPlayback(program2) {
10709
10895
  program2.command("playback").description(
10710
10896
  "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
10897
  ).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);
10898
+ const root = findProjectRoot41(opts.dir);
10899
+ const paths = resolveHaivePaths37(root);
10714
10900
  const events = await readUsageEvents5(paths);
10715
10901
  if (events.length === 0) {
10716
10902
  if (opts.json) {
@@ -10815,8 +11001,8 @@ function truncate3(text, max) {
10815
11001
  import { spawn as spawn4 } from "child_process";
10816
11002
  import "commander";
10817
11003
  import {
10818
- findProjectRoot as findProjectRoot41,
10819
- resolveHaivePaths as resolveHaivePaths37
11004
+ findProjectRoot as findProjectRoot42,
11005
+ resolveHaivePaths as resolveHaivePaths38
10820
11006
  } from "@hiveai/core";
10821
11007
  function registerPrecommit(program2) {
10822
11008
  program2.command("precommit").description(
@@ -10826,8 +11012,8 @@ function registerPrecommit(program2) {
10826
11012
  "'any' | 'high-confidence' (default) | 'never' (report only)",
10827
11013
  "high-confidence"
10828
11014
  ).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);
11015
+ const root = findProjectRoot42(opts.dir);
11016
+ const paths = resolveHaivePaths38(root);
10831
11017
  const ctx = { paths };
10832
11018
  let diff = "";
10833
11019
  let touchedPaths = opts.paths ?? [];
@@ -10922,9 +11108,9 @@ function runCommand3(cmd, args, cwd) {
10922
11108
  import { existsSync as existsSync63 } from "fs";
10923
11109
  import "commander";
10924
11110
  import {
10925
- findProjectRoot as findProjectRoot42,
11111
+ findProjectRoot as findProjectRoot43,
10926
11112
  loadMemoriesFromDir as loadMemoriesFromDir34,
10927
- resolveHaivePaths as resolveHaivePaths38
11113
+ resolveHaivePaths as resolveHaivePaths39
10928
11114
  } from "@hiveai/core";
10929
11115
  var TYPE_RANK = {
10930
11116
  decision: 0,
@@ -10938,8 +11124,8 @@ function registerWelcome(program2) {
10938
11124
  program2.command("welcome").description(
10939
11125
  "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
11126
  ).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);
11127
+ const root = findProjectRoot43(opts.dir);
11128
+ const paths = resolveHaivePaths39(root);
10943
11129
  if (!existsSync63(paths.memoriesDir)) {
10944
11130
  ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
10945
11131
  process.exitCode = 1;
@@ -10984,12 +11170,12 @@ function registerWelcome(program2) {
10984
11170
  import { existsSync as existsSync64 } from "fs";
10985
11171
  import "commander";
10986
11172
  import {
10987
- findProjectRoot as findProjectRoot43,
11173
+ findProjectRoot as findProjectRoot44,
10988
11174
  loadMemoriesFromDir as loadMemoriesFromDir35,
10989
- resolveHaivePaths as resolveHaivePaths39
11175
+ resolveHaivePaths as resolveHaivePaths40
10990
11176
  } from "@hiveai/core";
10991
11177
  async function lintMemoriesAsync(root) {
10992
- const paths = resolveHaivePaths39(root);
11178
+ const paths = resolveHaivePaths40(root);
10993
11179
  const out = [];
10994
11180
  if (!existsSync64(paths.memoriesDir)) return out;
10995
11181
  const loaded = await loadMemoriesFromDir35(paths.memoriesDir);
@@ -11052,7 +11238,7 @@ function registerMemoryLint(parent) {
11052
11238
  parent.command("lint").description(
11053
11239
  "Heuristic corpus checks (anchors on key types, headings, verbosity). Static analysis only."
11054
11240
  ).option("--json", "emit findings as JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
11055
- const root = findProjectRoot43(opts.dir);
11241
+ const root = findProjectRoot44(opts.dir);
11056
11242
  const findings = await lintMemoriesAsync(root);
11057
11243
  if (opts.json) {
11058
11244
  console.log(JSON.stringify({ findings_count: findings.length, findings }, null, 2));
@@ -11098,27 +11284,27 @@ function registerMemorySuggestTopic(memory2) {
11098
11284
  }
11099
11285
 
11100
11286
  // src/commands/resolve-project.ts
11101
- import path41 from "path";
11287
+ import path43 from "path";
11102
11288
  import "commander";
11103
11289
  import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
11104
11290
  function registerResolveProject(program2) {
11105
11291
  program2.command("resolve-project").description(
11106
11292
  "Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
11107
11293
  ).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
11108
- const info = resolveProjectInfo2({ cwd: path41.resolve(opts.dir) });
11294
+ const info = resolveProjectInfo2({ cwd: path43.resolve(opts.dir) });
11109
11295
  console.log(JSON.stringify({ ok: true, info }, null, 2));
11110
11296
  });
11111
11297
  }
11112
11298
 
11113
11299
  // src/commands/runtime-journal.ts
11114
11300
  import { existsSync as existsSync65 } from "fs";
11115
- import path43 from "path";
11301
+ import path44 from "path";
11116
11302
  import "commander";
11117
11303
  import {
11118
11304
  appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
11119
- findProjectRoot as findProjectRoot44,
11305
+ findProjectRoot as findProjectRoot45,
11120
11306
  readRuntimeJournalTail as readRuntimeJournalTail2,
11121
- resolveHaivePaths as resolveHaivePaths40
11307
+ resolveHaivePaths as resolveHaivePaths41
11122
11308
  } from "@hiveai/core";
11123
11309
  function registerRuntime(program2) {
11124
11310
  const runtime = program2.command("runtime").description(
@@ -11126,16 +11312,16 @@ function registerRuntime(program2) {
11126
11312
  );
11127
11313
  const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
11128
11314
  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));
11315
+ const root = path44.resolve(opts.dir ?? process.cwd());
11316
+ const paths = resolveHaivePaths41(findProjectRoot45(root));
11131
11317
  const raw = opts.kind ?? "note";
11132
11318
  const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
11133
11319
  await appendRuntimeJournalEntry3(paths, { kind, message });
11134
- ui.success(`Appended to ${path43.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
11320
+ ui.success(`Appended to ${path44.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
11135
11321
  });
11136
11322
  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));
11323
+ const root = path44.resolve(opts.dir ?? process.cwd());
11324
+ const paths = resolveHaivePaths41(findProjectRoot45(root));
11139
11325
  const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
11140
11326
  if (!existsSync65(paths.haiveDir)) {
11141
11327
  ui.error("No .ai/ \u2014 run `haive init` first.");
@@ -11153,12 +11339,12 @@ function registerRuntime(program2) {
11153
11339
 
11154
11340
  // src/commands/memory-timeline.ts
11155
11341
  import { existsSync as existsSync66 } from "fs";
11156
- import path44 from "path";
11342
+ import path45 from "path";
11157
11343
  import "commander";
11158
11344
  import {
11159
11345
  collectTimelineEntries as collectTimelineEntries2,
11160
- findProjectRoot as findProjectRoot45,
11161
- resolveHaivePaths as resolveHaivePaths41
11346
+ findProjectRoot as findProjectRoot46,
11347
+ resolveHaivePaths as resolveHaivePaths42
11162
11348
  } from "@hiveai/core";
11163
11349
  function registerMemoryTimeline(memory2) {
11164
11350
  memory2.command("timeline").description(
@@ -11169,8 +11355,8 @@ function registerMemoryTimeline(memory2) {
11169
11355
  process.exitCode = 1;
11170
11356
  return;
11171
11357
  }
11172
- const root = path44.resolve(opts.dir ?? process.cwd());
11173
- const paths = resolveHaivePaths41(findProjectRoot45(root));
11358
+ const root = path45.resolve(opts.dir ?? process.cwd());
11359
+ const paths = resolveHaivePaths42(findProjectRoot46(root));
11174
11360
  if (!existsSync66(paths.memoriesDir)) {
11175
11361
  ui.error("No memories \u2014 run `haive init`.");
11176
11362
  process.exitCode = 1;
@@ -11190,13 +11376,13 @@ function registerMemoryTimeline(memory2) {
11190
11376
 
11191
11377
  // src/commands/memory-conflict-candidates.ts
11192
11378
  import { existsSync as existsSync67 } from "fs";
11193
- import path45 from "path";
11379
+ import path46 from "path";
11194
11380
  import "commander";
11195
11381
  import {
11196
11382
  findLexicalConflictPairs as findLexicalConflictPairs2,
11197
11383
  findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
11198
- findProjectRoot as findProjectRoot46,
11199
- resolveHaivePaths as resolveHaivePaths42
11384
+ findProjectRoot as findProjectRoot47,
11385
+ resolveHaivePaths as resolveHaivePaths43
11200
11386
  } from "@hiveai/core";
11201
11387
  function parseTypes(csv) {
11202
11388
  const allowed = ["decision", "architecture", "convention", "gotcha"];
@@ -11212,8 +11398,8 @@ function registerMemoryConflictCandidates(memory2) {
11212
11398
  "decision,architecture,convention,gotcha (lexical scan)",
11213
11399
  "decision,architecture"
11214
11400
  ).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));
11401
+ const root = path46.resolve(opts.dir ?? process.cwd());
11402
+ const paths = resolveHaivePaths43(findProjectRoot47(root));
11217
11403
  if (!existsSync67(paths.memoriesDir)) {
11218
11404
  ui.error("No memories \u2014 run `haive init`.");
11219
11405
  process.exitCode = 1;
@@ -11251,11 +11437,11 @@ function registerMemoryConflictCandidates(memory2) {
11251
11437
  // src/commands/enforce.ts
11252
11438
  import { spawn as spawn5 } from "child_process";
11253
11439
  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";
11440
+ import { chmod as chmod2, mkdir as mkdir19, readFile as readFile17, rm as rm3, writeFile as writeFile31 } from "fs/promises";
11441
+ import path47 from "path";
11256
11442
  import "commander";
11257
11443
  import {
11258
- findProjectRoot as findProjectRoot47,
11444
+ findProjectRoot as findProjectRoot48,
11259
11445
  hasRecentBriefingMarker,
11260
11446
  isFreshIsoDate,
11261
11447
  loadConfig as loadConfig8,
@@ -11263,7 +11449,7 @@ import {
11263
11449
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
11264
11450
  readRecentBriefingMarker,
11265
11451
  resolveBriefingBudget as resolveBriefingBudget3,
11266
- resolveHaivePaths as resolveHaivePaths43,
11452
+ resolveHaivePaths as resolveHaivePaths44,
11267
11453
  saveConfig as saveConfig3,
11268
11454
  SESSION_RECAP_TTL_MS,
11269
11455
  verifyAnchor as verifyAnchor4,
@@ -11276,9 +11462,9 @@ function registerEnforce(program2) {
11276
11462
  "Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
11277
11463
  );
11278
11464
  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 });
11465
+ const root = findProjectRoot48(opts.dir);
11466
+ const paths = resolveHaivePaths44(root);
11467
+ await mkdir19(paths.haiveDir, { recursive: true });
11282
11468
  const current = await loadConfig8(paths);
11283
11469
  await saveConfig3(paths, {
11284
11470
  ...current,
@@ -11302,7 +11488,7 @@ function registerEnforce(program2) {
11302
11488
  if (opts.claude !== false) {
11303
11489
  try {
11304
11490
  const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
11305
- ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path46.relative(root, result.settingsPath)})`);
11491
+ ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path47.relative(root, result.settingsPath)})`);
11306
11492
  } catch (err) {
11307
11493
  ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
11308
11494
  }
@@ -11321,15 +11507,15 @@ function registerEnforce(program2) {
11321
11507
  if (report.should_block) process.exit(2);
11322
11508
  });
11323
11509
  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);
11510
+ const root = findProjectRoot48(opts.dir);
11511
+ const paths = resolveHaivePaths44(root);
11326
11512
  const targets = [
11327
- path46.join(paths.haiveDir, ".cache"),
11328
- path46.join(paths.haiveDir, ".runtime")
11513
+ path47.join(paths.haiveDir, ".cache"),
11514
+ path47.join(paths.haiveDir, ".runtime")
11329
11515
  ];
11330
11516
  for (const target of targets) {
11331
11517
  if (!existsSync68(target)) continue;
11332
- const rel = path46.relative(root, target);
11518
+ const rel = path47.relative(root, target);
11333
11519
  if (opts.dryRun) ui.info(`would remove ${rel}`);
11334
11520
  else {
11335
11521
  await rm3(target, { recursive: true, force: true });
@@ -11346,9 +11532,9 @@ function registerEnforce(program2) {
11346
11532
  const payload = await readHookPayload();
11347
11533
  const root = resolveRoot(opts.dir, payload);
11348
11534
  if (!root) return;
11349
- const paths = resolveHaivePaths43(root);
11535
+ const paths = resolveHaivePaths44(root);
11350
11536
  if (!existsSync68(paths.haiveDir)) return;
11351
- await mkdir18(paths.runtimeDir, { recursive: true });
11537
+ await mkdir19(paths.runtimeDir, { recursive: true });
11352
11538
  const sessionId = opts.sessionId ?? payload.session_id;
11353
11539
  const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
11354
11540
  const budget = resolveBriefingBudget3("quick", {
@@ -11408,7 +11594,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
11408
11594
  const payload = await readHookPayload();
11409
11595
  const root = resolveRoot(opts.dir, payload);
11410
11596
  if (!root) return;
11411
- const paths = resolveHaivePaths43(root);
11597
+ const paths = resolveHaivePaths44(root);
11412
11598
  if (!existsSync68(paths.haiveDir)) return;
11413
11599
  if (!isWriteLikeTool(payload)) return;
11414
11600
  const ok = await hasRecentBriefingMarker(paths, payload.session_id);
@@ -11431,8 +11617,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
11431
11617
  });
11432
11618
  }
11433
11619
  async function runWithEnforcement(command, args, opts) {
11434
- const root = findProjectRoot47(opts.dir);
11435
- const paths = resolveHaivePaths43(root);
11620
+ const root = findProjectRoot48(opts.dir);
11621
+ const paths = resolveHaivePaths44(root);
11436
11622
  if (!existsSync68(paths.haiveDir)) {
11437
11623
  ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
11438
11624
  process.exit(1);
@@ -11452,7 +11638,7 @@ async function runWithEnforcement(command, args, opts) {
11452
11638
  process.exit(2);
11453
11639
  }
11454
11640
  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`);
11641
+ ui.info(`Briefing written to ${path47.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
11456
11642
  const child = spawn5(command, args, {
11457
11643
  cwd: root,
11458
11644
  stdio: "inherit",
@@ -11501,9 +11687,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
11501
11687
  source: "haive-run",
11502
11688
  memoryIds: briefing.memories.map((m) => m.id)
11503
11689
  });
11504
- const dir = path46.join(paths.runtimeDir, "enforcement", "briefings");
11505
- await mkdir18(dir, { recursive: true });
11506
- const file = path46.join(dir, `${sessionId}.md`);
11690
+ const dir = path47.join(paths.runtimeDir, "enforcement", "briefings");
11691
+ await mkdir19(dir, { recursive: true });
11692
+ const file = path47.join(dir, `${sessionId}.md`);
11507
11693
  const parts = [
11508
11694
  "# hAIve Briefing",
11509
11695
  "",
@@ -11525,8 +11711,8 @@ async function writeWrapperBriefing(paths, sessionId, task) {
11525
11711
  return file;
11526
11712
  }
11527
11713
  async function buildEnforcementReport(dir, stage, sessionId) {
11528
- const root = findProjectRoot47(dir);
11529
- const paths = resolveHaivePaths43(root);
11714
+ const root = findProjectRoot48(dir);
11715
+ const paths = resolveHaivePaths44(root);
11530
11716
  const initialized = existsSync68(paths.haiveDir);
11531
11717
  const config = initialized ? await loadConfig8(paths) : {};
11532
11718
  const mode = config.enforcement?.mode ?? "strict";
@@ -11777,12 +11963,12 @@ function buildScore(findings, threshold = 80) {
11777
11963
  };
11778
11964
  }
11779
11965
  async function installGitEnforcement(root) {
11780
- const hooksDir = path46.join(root, ".git", "hooks");
11781
- if (!existsSync68(path46.join(root, ".git"))) {
11966
+ const hooksDir = path47.join(root, ".git", "hooks");
11967
+ if (!existsSync68(path47.join(root, ".git"))) {
11782
11968
  ui.warn("No .git directory found; git enforcement hooks skipped.");
11783
11969
  return;
11784
11970
  }
11785
- await mkdir18(hooksDir, { recursive: true });
11971
+ await mkdir19(hooksDir, { recursive: true });
11786
11972
  const hooks = [
11787
11973
  {
11788
11974
  name: "pre-commit",
@@ -11800,7 +11986,7 @@ haive enforce check --stage pre-push --dir . || exit $?
11800
11986
  }
11801
11987
  ];
11802
11988
  for (const hook of hooks) {
11803
- const file = path46.join(hooksDir, hook.name);
11989
+ const file = path47.join(hooksDir, hook.name);
11804
11990
  if (existsSync68(file)) {
11805
11991
  const current = await readFile17(file, "utf8").catch(() => "");
11806
11992
  if (current.includes(ENFORCE_HOOK_MARKER)) {
@@ -11818,8 +12004,8 @@ ${hook.body}`, "utf8");
11818
12004
  ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
11819
12005
  }
11820
12006
  async function installCiEnforcement(root) {
11821
- const workflowPath = path46.join(root, ".github", "workflows", "haive-enforcement.yml");
11822
- await mkdir18(path46.dirname(workflowPath), { recursive: true });
12007
+ const workflowPath = path47.join(root, ".github", "workflows", "haive-enforcement.yml");
12008
+ await mkdir19(path47.dirname(workflowPath), { recursive: true });
11823
12009
  if (existsSync68(workflowPath)) {
11824
12010
  ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
11825
12011
  return;
@@ -11848,7 +12034,7 @@ jobs:
11848
12034
  - name: Enforce hAIve policy
11849
12035
  run: haive enforce ci
11850
12036
  `, "utf8");
11851
- ui.success(`Created ${path46.relative(root, workflowPath)}`);
12037
+ ui.success(`Created ${path47.relative(root, workflowPath)}`);
11852
12038
  }
11853
12039
  function printReport(report, json) {
11854
12040
  if (json) {
@@ -11877,7 +12063,7 @@ async function readHookPayload() {
11877
12063
  }
11878
12064
  function resolveRoot(dir, payload) {
11879
12065
  try {
11880
- return findProjectRoot47(dir ?? payload.cwd);
12066
+ return findProjectRoot48(dir ?? payload.cwd);
11881
12067
  } catch {
11882
12068
  return null;
11883
12069
  }
@@ -11946,14 +12132,15 @@ function registerRun(program2) {
11946
12132
  }
11947
12133
 
11948
12134
  // 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");
12135
+ var program = new Command51();
12136
+ program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.12");
11951
12137
  registerInit(program);
11952
12138
  registerWelcome(program);
11953
12139
  registerResolveProject(program);
11954
12140
  registerRuntime(program);
11955
12141
  registerEnforce(program);
11956
12142
  registerRun(program);
12143
+ registerAgent(program);
11957
12144
  registerMcp(program);
11958
12145
  registerBriefing(program);
11959
12146
  registerTui(program);