@hiveai/cli 0.9.11 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/index.js +866 -671
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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
|
|
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(([
|
|
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
|
|
734
|
-
import { existsSync as
|
|
735
|
-
import
|
|
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
|
|
741
|
+
resolveHaivePaths as resolveHaivePaths5,
|
|
742
742
|
saveCodeMap as saveCodeMap2,
|
|
743
743
|
saveConfig
|
|
744
744
|
} from "@hiveai/core";
|
|
745
745
|
|
|
746
|
-
// src/commands/
|
|
747
|
-
import {
|
|
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 (
|
|
836
|
-
if (
|
|
837
|
-
if (
|
|
838
|
-
if (
|
|
839
|
-
if (
|
|
840
|
-
if (
|
|
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 (
|
|
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 =
|
|
1254
|
+
const rel = path6.relative(root, path6.join(dir, entry.name));
|
|
874
1255
|
results.push(rel);
|
|
875
|
-
await walk(
|
|
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 =
|
|
993
|
-
if (
|
|
1373
|
+
const pkgPath = path6.join(root, "package.json");
|
|
1374
|
+
if (existsSync5(pkgPath)) {
|
|
994
1375
|
try {
|
|
995
|
-
pkg = JSON.parse(await
|
|
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 ??
|
|
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 =
|
|
1010
|
-
if (
|
|
1390
|
+
const p = path6.join(root, name);
|
|
1391
|
+
if (existsSync5(p)) {
|
|
1011
1392
|
try {
|
|
1012
|
-
const content = await
|
|
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
|
|
1267
|
-
import { existsSync as
|
|
1268
|
-
import
|
|
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
|
|
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 (
|
|
2081
|
+
if (existsSync6(filePath)) continue;
|
|
1892
2082
|
const content = serializeMemory({ frontmatter: fm, body: mem.body });
|
|
1893
|
-
await
|
|
1894
|
-
await
|
|
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 =
|
|
2110
|
-
const paths =
|
|
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 (
|
|
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
|
|
2118
|
-
await
|
|
2119
|
-
await
|
|
2120
|
-
await
|
|
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 (!
|
|
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
|
|
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
|
|
2325
|
+
await writeFile4(paths.projectContext, PROJECT_CONTEXT_TEMPLATE, "utf8");
|
|
2132
2326
|
}
|
|
2133
2327
|
} else {
|
|
2134
|
-
await
|
|
2135
|
-
ui.success(`Created ${
|
|
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 =
|
|
2139
|
-
|
|
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,
|
|
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 =
|
|
2174
|
-
if (
|
|
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
|
|
2178
|
-
await
|
|
2179
|
-
ui.success(`Created ${
|
|
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 =
|
|
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
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
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
|
|
2219
|
-
|
|
2220
|
-
if (r.status === "
|
|
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
|
-
|
|
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 =
|
|
2283
|
-
if (!
|
|
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 =
|
|
2297
|
-
if (
|
|
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
|
|
2302
|
-
await
|
|
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 =
|
|
2307
|
-
if (
|
|
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
|
|
2312
|
-
await
|
|
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
|
|
2329
|
-
const gi =
|
|
2330
|
-
if (!
|
|
2331
|
-
await
|
|
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 =
|
|
2334
|
-
if (!
|
|
2335
|
-
await
|
|
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 =
|
|
2526
|
+
const gitignorePath = path8.join(root, ".gitignore");
|
|
2341
2527
|
let existing = "";
|
|
2342
|
-
if (
|
|
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
|
|
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
|
|
2356
|
-
import { existsSync as
|
|
2357
|
-
import
|
|
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
|
|
2545
|
+
import { findProjectRoot as findProjectRoot7 } from "@hiveai/core";
|
|
2360
2546
|
|
|
2361
2547
|
// src/utils/claude-hooks.ts
|
|
2362
|
-
import { existsSync as
|
|
2363
|
-
import { mkdir as
|
|
2364
|
-
import
|
|
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 (
|
|
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
|
|
2461
|
-
await
|
|
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 (!
|
|
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
|
|
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
|
|
2662
|
+
return path9.join(home, ".claude", "settings.json");
|
|
2477
2663
|
}
|
|
2478
|
-
return
|
|
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 =
|
|
2535
|
-
const gitDir =
|
|
2536
|
-
if (!
|
|
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 =
|
|
2542
|
-
await
|
|
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 =
|
|
2547
|
-
if (
|
|
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
|
|
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 =
|
|
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
|
|
2611
|
-
import { existsSync as
|
|
2612
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
2691
|
-
if (!
|
|
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 =
|
|
2701
|
-
await
|
|
2886
|
+
const cacheDir = path11.join(paths.haiveDir, ".cache");
|
|
2887
|
+
await mkdir8(cacheDir, { recursive: true });
|
|
2702
2888
|
await appendFile(
|
|
2703
|
-
|
|
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
|
|
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
|
|
2720
|
-
import { mkdir as
|
|
2721
|
-
import { existsSync as
|
|
2722
|
-
import
|
|
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
|
|
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
|
|
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 ??
|
|
2985
|
-
return { paths:
|
|
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 ?
|
|
2996
|
-
const exists =
|
|
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
|
|
3003
|
-
await
|
|
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 (!
|
|
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
|
|
4079
|
+
await writeFile72(file, serializeMemory6({ frontmatter, body }), "utf8");
|
|
3894
4080
|
return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
|
|
3895
4081
|
}
|
|
3896
4082
|
var MemObserveInputSchema = {
|
|
@@ -4120,6 +4306,7 @@ async function memSessionEnd(input, ctx) {
|
|
|
4120
4306
|
const revisionCount = (fm.revision_count ?? 0) + 1;
|
|
4121
4307
|
const newFrontmatter = {
|
|
4122
4308
|
...fm,
|
|
4309
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4123
4310
|
revision_count: revisionCount,
|
|
4124
4311
|
anchor: {
|
|
4125
4312
|
...fm.anchor,
|
|
@@ -5520,13 +5707,11 @@ async function preCommitCheck(input, ctx) {
|
|
|
5520
5707
|
const filesTouching = new Set(relevantMatches.map((m) => m.id));
|
|
5521
5708
|
const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
|
|
5522
5709
|
const blockOn = input.block_on;
|
|
5710
|
+
const blockingWarnings = apResult.warnings.filter(isBlockingWarning);
|
|
5523
5711
|
let should_block = false;
|
|
5524
5712
|
if (blockOn !== "never") {
|
|
5525
|
-
const high = apResult.warnings.filter(
|
|
5526
|
-
(w) => w.confidence === "authoritative" || w.confidence === "trusted"
|
|
5527
|
-
);
|
|
5528
5713
|
if (blockOn === "any" && (apResult.warnings.length > 0 || staleHits.length > 0)) should_block = true;
|
|
5529
|
-
if (blockOn === "high-confidence" && (
|
|
5714
|
+
if (blockOn === "high-confidence" && (blockingWarnings.length > 0 || staleHits.length > 0)) should_block = true;
|
|
5530
5715
|
}
|
|
5531
5716
|
const relevant_memories = relevantMatches.slice(0, 8).map((m) => ({
|
|
5532
5717
|
id: m.id,
|
|
@@ -5538,6 +5723,7 @@ async function preCommitCheck(input, ctx) {
|
|
|
5538
5723
|
should_block,
|
|
5539
5724
|
summary: {
|
|
5540
5725
|
anti_patterns: apResult.warnings.length,
|
|
5726
|
+
blocking_warnings: blockingWarnings.length,
|
|
5541
5727
|
relevant_memories: relevant_memories.length,
|
|
5542
5728
|
stale_anchors: staleHits.length
|
|
5543
5729
|
},
|
|
@@ -5551,6 +5737,11 @@ async function preCommitCheck(input, ctx) {
|
|
|
5551
5737
|
}))
|
|
5552
5738
|
};
|
|
5553
5739
|
}
|
|
5740
|
+
function isBlockingWarning(warning) {
|
|
5741
|
+
const highConfidence = warning.confidence === "authoritative" || warning.confidence === "trusted";
|
|
5742
|
+
if (!highConfidence) return false;
|
|
5743
|
+
return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
|
|
5744
|
+
}
|
|
5554
5745
|
var CONFIG_PATTERNS = [
|
|
5555
5746
|
".eslintrc",
|
|
5556
5747
|
"eslint.config",
|
|
@@ -6082,7 +6273,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
6082
6273
|
};
|
|
6083
6274
|
}
|
|
6084
6275
|
var SERVER_NAME = "haive";
|
|
6085
|
-
var SERVER_VERSION = "0.9.
|
|
6276
|
+
var SERVER_VERSION = "0.9.13";
|
|
6086
6277
|
function jsonResult(data) {
|
|
6087
6278
|
return {
|
|
6088
6279
|
content: [
|
|
@@ -6991,7 +7182,7 @@ function registerMcp(program2) {
|
|
|
6991
7182
|
).option("-d, --dir <dir>", "project root (walks up from here for .ai/ / .git/)").option("-r, --root <dir>", "same as --dir (parity with legacy haive-mcp --root)").option("--stdio", "optional marker for client configs \u2014 transport is always stdio", false).action(async (opts) => {
|
|
6992
7183
|
void opts.stdio;
|
|
6993
7184
|
const raw = opts.root ?? opts.dir;
|
|
6994
|
-
const root = raw ?
|
|
7185
|
+
const root = raw ? findProjectRoot10(raw) : findProjectRoot10();
|
|
6995
7186
|
try {
|
|
6996
7187
|
await runHaiveMcpStdio({ root });
|
|
6997
7188
|
} catch (err) {
|
|
@@ -7002,15 +7193,15 @@ function registerMcp(program2) {
|
|
|
7002
7193
|
}
|
|
7003
7194
|
|
|
7004
7195
|
// src/commands/sync.ts
|
|
7005
|
-
import { spawnSync as
|
|
7006
|
-
import { readFile as readFile8, writeFile as writeFile13, mkdir as
|
|
7196
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
7197
|
+
import { readFile as readFile8, writeFile as writeFile13, mkdir as mkdir10 } from "fs/promises";
|
|
7007
7198
|
import { existsSync as existsSync29 } from "fs";
|
|
7008
|
-
import
|
|
7199
|
+
import path13 from "path";
|
|
7009
7200
|
import "commander";
|
|
7010
7201
|
import {
|
|
7011
7202
|
DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE2,
|
|
7012
7203
|
buildFrontmatter as buildFrontmatter6,
|
|
7013
|
-
findProjectRoot as
|
|
7204
|
+
findProjectRoot as findProjectRoot11,
|
|
7014
7205
|
getUsage as getUsage10,
|
|
7015
7206
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
7016
7207
|
isDecaying as isDecaying2,
|
|
@@ -7019,7 +7210,7 @@ import {
|
|
|
7019
7210
|
loadMemoriesFromDir as loadMemoriesFromDir23,
|
|
7020
7211
|
loadUsageIndex as loadUsageIndex12,
|
|
7021
7212
|
pullCrossRepoSources,
|
|
7022
|
-
resolveHaivePaths as
|
|
7213
|
+
resolveHaivePaths as resolveHaivePaths8,
|
|
7023
7214
|
resolveManifestFiles,
|
|
7024
7215
|
serializeMemory as serializeMemory11,
|
|
7025
7216
|
trackDependencies,
|
|
@@ -7038,8 +7229,8 @@ function registerSync(program2) {
|
|
|
7038
7229
|
"--inject-bridge",
|
|
7039
7230
|
"inject top validated memories into CLAUDE.md (or --bridge-file) between <!-- haive:memories-start/end --> markers"
|
|
7040
7231
|
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").action(async (opts) => {
|
|
7041
|
-
const root =
|
|
7042
|
-
const paths =
|
|
7232
|
+
const root = findProjectRoot11(opts.dir);
|
|
7233
|
+
const paths = resolveHaivePaths8(root);
|
|
7043
7234
|
if (!existsSync29(paths.memoriesDir)) {
|
|
7044
7235
|
if (!opts.quiet) ui.warn(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
7045
7236
|
process.exitCode = 1;
|
|
@@ -7172,7 +7363,7 @@ function registerSync(program2) {
|
|
|
7172
7363
|
);
|
|
7173
7364
|
}
|
|
7174
7365
|
if (opts.injectBridge) {
|
|
7175
|
-
const bridgeFile = opts.bridgeFile ?
|
|
7366
|
+
const bridgeFile = opts.bridgeFile ? path13.resolve(opts.bridgeFile) : path13.join(root, "CLAUDE.md");
|
|
7176
7367
|
const maxInject = Math.max(1, Number(opts.bridgeMaxMemories ?? 5));
|
|
7177
7368
|
await injectBridge(bridgeFile, paths.memoriesDir, maxInject, root, opts.quiet);
|
|
7178
7369
|
}
|
|
@@ -7278,10 +7469,10 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
7278
7469
|
paths: [result.file],
|
|
7279
7470
|
topic: `dep-bump-${slugParts}`
|
|
7280
7471
|
});
|
|
7281
|
-
const teamDir =
|
|
7282
|
-
await
|
|
7472
|
+
const teamDir = path13.join(paths.memoriesDir, "team");
|
|
7473
|
+
await mkdir10(teamDir, { recursive: true });
|
|
7283
7474
|
await writeFile13(
|
|
7284
|
-
|
|
7475
|
+
path13.join(teamDir, `${fm.id}.md`),
|
|
7285
7476
|
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
7286
7477
|
"utf8"
|
|
7287
7478
|
);
|
|
@@ -7345,10 +7536,10 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
7345
7536
|
paths: [diff.file],
|
|
7346
7537
|
topic: `contract-breaking-${diff.contract}`
|
|
7347
7538
|
});
|
|
7348
|
-
const teamDir =
|
|
7349
|
-
await
|
|
7539
|
+
const teamDir = path13.join(paths.memoriesDir, "team");
|
|
7540
|
+
await mkdir10(teamDir, { recursive: true });
|
|
7350
7541
|
await writeFile13(
|
|
7351
|
-
|
|
7542
|
+
path13.join(teamDir, `${fm.id}.md`),
|
|
7352
7543
|
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
7353
7544
|
"utf8"
|
|
7354
7545
|
);
|
|
@@ -7362,7 +7553,7 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
7362
7553
|
const existingMap = await loadCodeMap4(paths);
|
|
7363
7554
|
if (existingMap) {
|
|
7364
7555
|
const mapAge = new Date(existingMap.generated_at).getTime();
|
|
7365
|
-
const gitResult =
|
|
7556
|
+
const gitResult = spawnSync3(
|
|
7366
7557
|
"git",
|
|
7367
7558
|
[
|
|
7368
7559
|
"diff",
|
|
@@ -7441,11 +7632,11 @@ ${BRIDGE_END}`;
|
|
|
7441
7632
|
const startIdx = existing.indexOf(BRIDGE_START);
|
|
7442
7633
|
const endIdx = existing.indexOf(BRIDGE_END);
|
|
7443
7634
|
if (startIdx !== -1 && endIdx === -1) {
|
|
7444
|
-
ui.warn(`${
|
|
7635
|
+
ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_START} without ${BRIDGE_END}. Fix the file manually before running --inject-bridge.`);
|
|
7445
7636
|
return;
|
|
7446
7637
|
}
|
|
7447
7638
|
if (startIdx === -1 && endIdx !== -1) {
|
|
7448
|
-
ui.warn(`${
|
|
7639
|
+
ui.warn(`${path13.relative(root, bridgeFile)}: found ${BRIDGE_END} without ${BRIDGE_START}. Fix the file manually before running --inject-bridge.`);
|
|
7449
7640
|
return;
|
|
7450
7641
|
}
|
|
7451
7642
|
let updated;
|
|
@@ -7453,19 +7644,19 @@ ${BRIDGE_END}`;
|
|
|
7453
7644
|
updated = existing.slice(0, startIdx) + injected + existing.slice(endIdx + BRIDGE_END.length);
|
|
7454
7645
|
} else {
|
|
7455
7646
|
if (!fileExists && !quiet) {
|
|
7456
|
-
ui.info(`Creating ${
|
|
7647
|
+
ui.info(`Creating ${path13.relative(root, bridgeFile)} with haive memory block.`);
|
|
7457
7648
|
}
|
|
7458
7649
|
updated = existing + (existing.endsWith("\n") ? "" : "\n") + "\n" + injected + "\n";
|
|
7459
7650
|
}
|
|
7460
7651
|
await writeFile13(bridgeFile, updated, "utf8");
|
|
7461
7652
|
if (!quiet) {
|
|
7462
7653
|
console.log(
|
|
7463
|
-
ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${
|
|
7654
|
+
ui.dim(`bridge: injected ${top.length} memor${top.length === 1 ? "y" : "ies"} into ${path13.relative(root, bridgeFile)}`)
|
|
7464
7655
|
);
|
|
7465
7656
|
}
|
|
7466
7657
|
}
|
|
7467
7658
|
function collectSinceChanges(root, ref) {
|
|
7468
|
-
const result =
|
|
7659
|
+
const result = spawnSync3(
|
|
7469
7660
|
"git",
|
|
7470
7661
|
["-C", root, "diff", "--name-status", "--diff-filter=AMD", `${ref}...HEAD`, "--", ".ai/memories"],
|
|
7471
7662
|
{ encoding: "utf8" }
|
|
@@ -7485,17 +7676,17 @@ function collectSinceChanges(root, ref) {
|
|
|
7485
7676
|
|
|
7486
7677
|
// src/commands/memory-add.ts
|
|
7487
7678
|
import { createHash as createHash2 } from "crypto";
|
|
7488
|
-
import { mkdir as
|
|
7679
|
+
import { mkdir as mkdir11, readFile as readFile9, writeFile as writeFile14 } from "fs/promises";
|
|
7489
7680
|
import { existsSync as existsSync30 } from "fs";
|
|
7490
|
-
import
|
|
7681
|
+
import path14 from "path";
|
|
7491
7682
|
import "commander";
|
|
7492
7683
|
import {
|
|
7493
7684
|
buildFrontmatter as buildFrontmatter7,
|
|
7494
|
-
findProjectRoot as
|
|
7685
|
+
findProjectRoot as findProjectRoot12,
|
|
7495
7686
|
inferModulesFromPaths as inferModulesFromPaths3,
|
|
7496
7687
|
loadMemoriesFromDir as loadMemoriesFromDir24,
|
|
7497
7688
|
memoryFilePath as memoryFilePath6,
|
|
7498
|
-
resolveHaivePaths as
|
|
7689
|
+
resolveHaivePaths as resolveHaivePaths9,
|
|
7499
7690
|
serializeMemory as serializeMemory12
|
|
7500
7691
|
} from "@hiveai/core";
|
|
7501
7692
|
function registerMemoryAdd(memory2) {
|
|
@@ -7523,8 +7714,8 @@ function registerMemoryAdd(memory2) {
|
|
|
7523
7714
|
--scope team --body "Never modify existing migrations. Create V{n+1}__desc.sql."
|
|
7524
7715
|
`
|
|
7525
7716
|
).requiredOption("--type <type>", "convention | decision | gotcha | architecture | glossary | attempt").requiredOption("--slug <slug>", "short kebab-case identifier used in the file name").option("--title <text>", "memory title \u2014 becomes the first heading of the body").option("--scope <scope>", "personal | team | module (default: personal, or team in autopilot)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags for easier retrieval").option("--domain <domain>", "domain (e.g. transactions)").option("--author <author>", "author email or handle").option("--paths <csv>", "anchor to source files \u2014 used for staleness detection by haive sync").option("--symbols <csv>", "anchor to specific symbols (class/function names)").option("--commit <sha>", "anchor to a specific commit SHA").option("--body <text>", "memory body content (Markdown) \u2014 overrides --title default body").option("--body-file <path>", "read memory body from a Markdown file \u2014 for long content").option("--no-auto-tag", "disable automatic tag suggestions inferred from anchor paths").option("--topic <key>", "stable key for upsert: if a memory with this topic+scope already exists, update it in-place (revision_count++)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7526
|
-
const root =
|
|
7527
|
-
const paths =
|
|
7717
|
+
const root = findProjectRoot12(opts.dir);
|
|
7718
|
+
const paths = resolveHaivePaths9(root);
|
|
7528
7719
|
if (!existsSync30(paths.haiveDir)) {
|
|
7529
7720
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
7530
7721
|
process.exitCode = 1;
|
|
@@ -7536,7 +7727,7 @@ function registerMemoryAdd(memory2) {
|
|
|
7536
7727
|
const inferredTags = autoTagsEnabled ? inferModulesFromPaths3(anchorPaths) : [];
|
|
7537
7728
|
const mergedTags = Array.from(/* @__PURE__ */ new Set([...userTags, ...inferredTags]));
|
|
7538
7729
|
if (anchorPaths.length > 0) {
|
|
7539
|
-
const missing = anchorPaths.filter((p) => !existsSync30(
|
|
7730
|
+
const missing = anchorPaths.filter((p) => !existsSync30(path14.resolve(root, p)));
|
|
7540
7731
|
if (missing.length > 0) {
|
|
7541
7732
|
ui.warn(`Anchor path${missing.length > 1 ? "s" : ""} not found in project:`);
|
|
7542
7733
|
for (const p of missing) ui.warn(` \u2717 ${p}`);
|
|
@@ -7601,7 +7792,7 @@ TODO \u2014 write the memory body.
|
|
|
7601
7792
|
}
|
|
7602
7793
|
};
|
|
7603
7794
|
await writeFile14(topicMatch.filePath, serializeMemory12({ frontmatter: newFrontmatter, body }), "utf8");
|
|
7604
|
-
ui.success(`Updated (topic upsert) ${
|
|
7795
|
+
ui.success(`Updated (topic upsert) ${path14.relative(root, topicMatch.filePath)}`);
|
|
7605
7796
|
ui.info(`id=${fm.id} revision=${revisionCount}`);
|
|
7606
7797
|
return;
|
|
7607
7798
|
}
|
|
@@ -7620,7 +7811,7 @@ TODO \u2014 write the memory body.
|
|
|
7620
7811
|
topic: opts.topic
|
|
7621
7812
|
});
|
|
7622
7813
|
const file = memoryFilePath6(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
7623
|
-
await
|
|
7814
|
+
await mkdir11(path14.dirname(file), { recursive: true });
|
|
7624
7815
|
if (existsSync30(file)) {
|
|
7625
7816
|
ui.error(`Memory already exists at ${file}`);
|
|
7626
7817
|
process.exitCode = 1;
|
|
@@ -7639,7 +7830,7 @@ TODO \u2014 write the memory body.
|
|
|
7639
7830
|
}
|
|
7640
7831
|
}
|
|
7641
7832
|
await writeFile14(file, serializeMemory12({ frontmatter, body }), "utf8");
|
|
7642
|
-
ui.success(`Created ${
|
|
7833
|
+
ui.success(`Created ${path14.relative(root, file)}`);
|
|
7643
7834
|
ui.info(`id=${frontmatter.id} scope=${frontmatter.scope} status=${frontmatter.status}`);
|
|
7644
7835
|
if (inferredTags.length > 0) {
|
|
7645
7836
|
ui.info(`auto-tagged: ${inferredTags.join(", ")} (use --no-auto-tag to disable)`);
|
|
@@ -7670,9 +7861,9 @@ function parseCsv2(value) {
|
|
|
7670
7861
|
|
|
7671
7862
|
// src/commands/memory-list.ts
|
|
7672
7863
|
import { existsSync as existsSync31 } from "fs";
|
|
7673
|
-
import
|
|
7864
|
+
import path15 from "path";
|
|
7674
7865
|
import "commander";
|
|
7675
|
-
import { findProjectRoot as
|
|
7866
|
+
import { findProjectRoot as findProjectRoot13, resolveHaivePaths as resolveHaivePaths10 } from "@hiveai/core";
|
|
7676
7867
|
|
|
7677
7868
|
// src/utils/fs.ts
|
|
7678
7869
|
import {
|
|
@@ -7684,8 +7875,8 @@ import {
|
|
|
7684
7875
|
// src/commands/memory-list.ts
|
|
7685
7876
|
function registerMemoryList(memory2) {
|
|
7686
7877
|
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7687
|
-
const root =
|
|
7688
|
-
const paths =
|
|
7878
|
+
const root = findProjectRoot13(opts.dir);
|
|
7879
|
+
const paths = resolveHaivePaths10(root);
|
|
7689
7880
|
if (!existsSync31(paths.memoriesDir)) {
|
|
7690
7881
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
7691
7882
|
process.exitCode = 1;
|
|
@@ -7718,7 +7909,7 @@ function registerMemoryList(memory2) {
|
|
|
7718
7909
|
console.log(
|
|
7719
7910
|
`${ui.bold(fm.id)} ${ui.dim(fm.scope)}/${ui.dim(fm.type)} ${statusBadge}${moduleStr}${tagStr}`
|
|
7720
7911
|
);
|
|
7721
|
-
console.log(` ${ui.dim(
|
|
7912
|
+
console.log(` ${ui.dim(path15.relative(root, filePath))}`);
|
|
7722
7913
|
}
|
|
7723
7914
|
console.log(ui.dim(`
|
|
7724
7915
|
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`));
|
|
@@ -7753,20 +7944,20 @@ function matchesFilters(loaded, opts) {
|
|
|
7753
7944
|
}
|
|
7754
7945
|
|
|
7755
7946
|
// src/commands/memory-promote.ts
|
|
7756
|
-
import { mkdir as
|
|
7947
|
+
import { mkdir as mkdir12, unlink as unlink2, writeFile as writeFile15 } from "fs/promises";
|
|
7757
7948
|
import { existsSync as existsSync33 } from "fs";
|
|
7758
|
-
import
|
|
7949
|
+
import path16 from "path";
|
|
7759
7950
|
import "commander";
|
|
7760
7951
|
import {
|
|
7761
|
-
findProjectRoot as
|
|
7952
|
+
findProjectRoot as findProjectRoot14,
|
|
7762
7953
|
memoryFilePath as memoryFilePath7,
|
|
7763
|
-
resolveHaivePaths as
|
|
7954
|
+
resolveHaivePaths as resolveHaivePaths11,
|
|
7764
7955
|
serializeMemory as serializeMemory13
|
|
7765
7956
|
} from "@hiveai/core";
|
|
7766
7957
|
function registerMemoryPromote(memory2) {
|
|
7767
7958
|
memory2.command("promote <id>").description("Promote a personal memory to team scope (status -> proposed)").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7768
|
-
const root =
|
|
7769
|
-
const paths =
|
|
7959
|
+
const root = findProjectRoot14(opts.dir);
|
|
7960
|
+
const paths = resolveHaivePaths11(root);
|
|
7770
7961
|
if (!existsSync33(paths.memoriesDir)) {
|
|
7771
7962
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
7772
7963
|
process.exitCode = 1;
|
|
@@ -7802,11 +7993,11 @@ function registerMemoryPromote(memory2) {
|
|
|
7802
7993
|
body: found.memory.body
|
|
7803
7994
|
};
|
|
7804
7995
|
const newPath = memoryFilePath7(paths, "team", updated.frontmatter.id);
|
|
7805
|
-
await
|
|
7996
|
+
await mkdir12(path16.dirname(newPath), { recursive: true });
|
|
7806
7997
|
await writeFile15(newPath, serializeMemory13(updated), "utf8");
|
|
7807
7998
|
await unlink2(found.filePath);
|
|
7808
7999
|
ui.success(`Promoted ${id} to team scope (status=proposed)`);
|
|
7809
|
-
ui.info(`Now at ${
|
|
8000
|
+
ui.info(`Now at ${path16.relative(root, newPath)}`);
|
|
7810
8001
|
console.log(ui.dim(`\u2192 next: haive memory approve ${id} (validate for team use)`));
|
|
7811
8002
|
});
|
|
7812
8003
|
}
|
|
@@ -7814,17 +8005,17 @@ function registerMemoryPromote(memory2) {
|
|
|
7814
8005
|
// src/commands/memory-approve.ts
|
|
7815
8006
|
import { existsSync as existsSync34 } from "fs";
|
|
7816
8007
|
import { writeFile as writeFile16 } from "fs/promises";
|
|
7817
|
-
import
|
|
8008
|
+
import path17 from "path";
|
|
7818
8009
|
import "commander";
|
|
7819
8010
|
import {
|
|
7820
|
-
findProjectRoot as
|
|
7821
|
-
resolveHaivePaths as
|
|
8011
|
+
findProjectRoot as findProjectRoot15,
|
|
8012
|
+
resolveHaivePaths as resolveHaivePaths12,
|
|
7822
8013
|
serializeMemory as serializeMemory14
|
|
7823
8014
|
} from "@hiveai/core";
|
|
7824
8015
|
function registerMemoryApprove(memory2) {
|
|
7825
8016
|
memory2.command("approve [id]").description("Mark a memory as 'validated'. Use --all to bulk-approve all proposed/draft memories.").option("--all", "approve all proposed and draft memories at once").option("--pending", "approve all memories with status 'proposed'").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7826
|
-
const root =
|
|
7827
|
-
const paths =
|
|
8017
|
+
const root = findProjectRoot15(opts.dir);
|
|
8018
|
+
const paths = resolveHaivePaths12(root);
|
|
7828
8019
|
if (!existsSync34(paths.memoriesDir)) {
|
|
7829
8020
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7830
8021
|
process.exitCode = 1;
|
|
@@ -7878,24 +8069,24 @@ function registerMemoryApprove(memory2) {
|
|
|
7878
8069
|
};
|
|
7879
8070
|
await writeFile16(found.filePath, serializeMemory14(next), "utf8");
|
|
7880
8071
|
ui.success(`Approved ${id} (status=validated)`);
|
|
7881
|
-
ui.info(
|
|
8072
|
+
ui.info(path17.relative(root, found.filePath));
|
|
7882
8073
|
});
|
|
7883
8074
|
}
|
|
7884
8075
|
|
|
7885
8076
|
// src/commands/memory-update.ts
|
|
7886
8077
|
import { writeFile as writeFile17 } from "fs/promises";
|
|
7887
8078
|
import { existsSync as existsSync35 } from "fs";
|
|
7888
|
-
import
|
|
8079
|
+
import path18 from "path";
|
|
7889
8080
|
import "commander";
|
|
7890
8081
|
import {
|
|
7891
|
-
findProjectRoot as
|
|
7892
|
-
resolveHaivePaths as
|
|
8082
|
+
findProjectRoot as findProjectRoot16,
|
|
8083
|
+
resolveHaivePaths as resolveHaivePaths13,
|
|
7893
8084
|
serializeMemory as serializeMemory15
|
|
7894
8085
|
} from "@hiveai/core";
|
|
7895
8086
|
function registerMemoryUpdate(memory2) {
|
|
7896
8087
|
memory2.command("update <id>").description("Update body, tags, or anchor of an existing memory (preserves id and usage history)").option("--title <text>", "new title \u2014 replaces the first heading of the body").option("--body <text>", "new Markdown body \u2014 replaces the existing body").option("--tags <csv>", "new tags, comma-separated \u2014 fully replaces existing tags").option("--paths <csv>", "new anchor paths, comma-separated").option("--symbols <csv>", "new anchor symbols, comma-separated").option("--commit <sha>", "new anchor commit SHA").option("--domain <domain>", "new domain label").option("--author <author>", "new author handle or email").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
7897
|
-
const root =
|
|
7898
|
-
const paths =
|
|
8088
|
+
const root = findProjectRoot16(opts.dir);
|
|
8089
|
+
const paths = resolveHaivePaths13(root);
|
|
7899
8090
|
if (!existsSync35(paths.memoriesDir)) {
|
|
7900
8091
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
7901
8092
|
process.exitCode = 1;
|
|
@@ -7948,7 +8139,7 @@ function registerMemoryUpdate(memory2) {
|
|
|
7948
8139
|
serializeMemory15({ frontmatter: newFrontmatter, body: newBody }),
|
|
7949
8140
|
"utf8"
|
|
7950
8141
|
);
|
|
7951
|
-
ui.success(`Updated ${
|
|
8142
|
+
ui.success(`Updated ${path18.relative(root, loaded.filePath)}`);
|
|
7952
8143
|
ui.info(`fields: ${updated.join(", ")}`);
|
|
7953
8144
|
});
|
|
7954
8145
|
}
|
|
@@ -7969,15 +8160,15 @@ function parseCsv3(value) {
|
|
|
7969
8160
|
// src/commands/memory-auto-promote.ts
|
|
7970
8161
|
import { writeFile as writeFile18 } from "fs/promises";
|
|
7971
8162
|
import { existsSync as existsSync36 } from "fs";
|
|
7972
|
-
import
|
|
8163
|
+
import path19 from "path";
|
|
7973
8164
|
import "commander";
|
|
7974
8165
|
import {
|
|
7975
8166
|
DEFAULT_AUTO_PROMOTE_RULE as DEFAULT_AUTO_PROMOTE_RULE3,
|
|
7976
|
-
findProjectRoot as
|
|
8167
|
+
findProjectRoot as findProjectRoot17,
|
|
7977
8168
|
getUsage as getUsage11,
|
|
7978
8169
|
isAutoPromoteEligible as isAutoPromoteEligible3,
|
|
7979
8170
|
loadUsageIndex as loadUsageIndex13,
|
|
7980
|
-
resolveHaivePaths as
|
|
8171
|
+
resolveHaivePaths as resolveHaivePaths14,
|
|
7981
8172
|
serializeMemory as serializeMemory16
|
|
7982
8173
|
} from "@hiveai/core";
|
|
7983
8174
|
function registerMemoryAutoPromote(memory2) {
|
|
@@ -7986,8 +8177,8 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
7986
8177
|
"memories with more rejections than this are skipped",
|
|
7987
8178
|
String(DEFAULT_AUTO_PROMOTE_RULE3.maxRejections)
|
|
7988
8179
|
).option("--apply", "actually write status=validated to disk (default: dry-run)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
7989
|
-
const root =
|
|
7990
|
-
const paths =
|
|
8180
|
+
const root = findProjectRoot17(opts.dir);
|
|
8181
|
+
const paths = resolveHaivePaths14(root);
|
|
7991
8182
|
if (!existsSync36(paths.memoriesDir)) {
|
|
7992
8183
|
ui.error(`No .ai/memories at ${root}.`);
|
|
7993
8184
|
process.exitCode = 1;
|
|
@@ -8014,7 +8205,7 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
8014
8205
|
console.log(
|
|
8015
8206
|
`${ui.bold(opts.apply ? "PROMOTE" : "would promote")} ${mem.frontmatter.id} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
8016
8207
|
);
|
|
8017
|
-
console.log(` ${ui.dim(
|
|
8208
|
+
console.log(` ${ui.dim(path19.relative(root, filePath))}`);
|
|
8018
8209
|
if (opts.apply) {
|
|
8019
8210
|
const next = {
|
|
8020
8211
|
frontmatter: { ...mem.frontmatter, status: "validated" },
|
|
@@ -8033,17 +8224,17 @@ function registerMemoryAutoPromote(memory2) {
|
|
|
8033
8224
|
import { spawn as spawn3 } from "child_process";
|
|
8034
8225
|
import { existsSync as existsSync37 } from "fs";
|
|
8035
8226
|
import { readFile as readFile10 } from "fs/promises";
|
|
8036
|
-
import
|
|
8227
|
+
import path20 from "path";
|
|
8037
8228
|
import "commander";
|
|
8038
8229
|
import {
|
|
8039
|
-
findProjectRoot as
|
|
8230
|
+
findProjectRoot as findProjectRoot18,
|
|
8040
8231
|
parseMemory,
|
|
8041
|
-
resolveHaivePaths as
|
|
8232
|
+
resolveHaivePaths as resolveHaivePaths15
|
|
8042
8233
|
} from "@hiveai/core";
|
|
8043
8234
|
function registerMemoryEdit(memory2) {
|
|
8044
8235
|
memory2.command("edit <id>").description("Open a memory in $EDITOR and re-validate when you save").option("-e, --editor <cmd>", "editor command (defaults to $EDITOR or 'vi')").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8045
|
-
const root =
|
|
8046
|
-
const paths =
|
|
8236
|
+
const root = findProjectRoot18(opts.dir);
|
|
8237
|
+
const paths = resolveHaivePaths15(root);
|
|
8047
8238
|
if (!existsSync37(paths.memoriesDir)) {
|
|
8048
8239
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8049
8240
|
process.exitCode = 1;
|
|
@@ -8057,7 +8248,7 @@ function registerMemoryEdit(memory2) {
|
|
|
8057
8248
|
return;
|
|
8058
8249
|
}
|
|
8059
8250
|
const editor = opts.editor ?? process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
8060
|
-
ui.info(`Opening ${
|
|
8251
|
+
ui.info(`Opening ${path20.relative(root, found.filePath)} with ${editor}\u2026`);
|
|
8061
8252
|
const code = await runEditor(editor, found.filePath);
|
|
8062
8253
|
if (code !== 0) {
|
|
8063
8254
|
ui.warn(`Editor exited with status ${code}.`);
|
|
@@ -8085,21 +8276,21 @@ function runEditor(editor, file) {
|
|
|
8085
8276
|
|
|
8086
8277
|
// src/commands/memory-for-files.ts
|
|
8087
8278
|
import { existsSync as existsSync38 } from "fs";
|
|
8088
|
-
import
|
|
8279
|
+
import path21 from "path";
|
|
8089
8280
|
import "commander";
|
|
8090
8281
|
import {
|
|
8091
8282
|
deriveConfidence as deriveConfidence9,
|
|
8092
|
-
findProjectRoot as
|
|
8283
|
+
findProjectRoot as findProjectRoot19,
|
|
8093
8284
|
getUsage as getUsage12,
|
|
8094
8285
|
inferModulesFromPaths as inferModulesFromPaths4,
|
|
8095
8286
|
loadUsageIndex as loadUsageIndex14,
|
|
8096
8287
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths5,
|
|
8097
|
-
resolveHaivePaths as
|
|
8288
|
+
resolveHaivePaths as resolveHaivePaths16
|
|
8098
8289
|
} from "@hiveai/core";
|
|
8099
8290
|
function registerMemoryForFiles(memory2) {
|
|
8100
8291
|
memory2.command("for-files <files...>").description("Show memories relevant to the given files (anchor overlap, module, domain)").option("-d, --dir <dir>", "project root").action(async (files, opts) => {
|
|
8101
|
-
const root =
|
|
8102
|
-
const paths =
|
|
8292
|
+
const root = findProjectRoot19(opts.dir);
|
|
8293
|
+
const paths = resolveHaivePaths16(root);
|
|
8103
8294
|
if (!existsSync38(paths.memoriesDir)) {
|
|
8104
8295
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8105
8296
|
process.exitCode = 1;
|
|
@@ -8207,24 +8398,24 @@ function printGroup(root, label, loaded, usage) {
|
|
|
8207
8398
|
const u = getUsage12(usage, fm.id);
|
|
8208
8399
|
const conf = deriveConfidence9(fm, u);
|
|
8209
8400
|
console.log(`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(conf)}`);
|
|
8210
|
-
console.log(` ${ui.dim(
|
|
8401
|
+
console.log(` ${ui.dim(path21.relative(root, filePath))}`);
|
|
8211
8402
|
}
|
|
8212
8403
|
}
|
|
8213
8404
|
|
|
8214
8405
|
// src/commands/memory-hot.ts
|
|
8215
8406
|
import { existsSync as existsSync39 } from "fs";
|
|
8216
|
-
import
|
|
8407
|
+
import path23 from "path";
|
|
8217
8408
|
import "commander";
|
|
8218
8409
|
import {
|
|
8219
|
-
findProjectRoot as
|
|
8410
|
+
findProjectRoot as findProjectRoot20,
|
|
8220
8411
|
getUsage as getUsage13,
|
|
8221
8412
|
loadUsageIndex as loadUsageIndex15,
|
|
8222
|
-
resolveHaivePaths as
|
|
8413
|
+
resolveHaivePaths as resolveHaivePaths17
|
|
8223
8414
|
} from "@hiveai/core";
|
|
8224
8415
|
function registerMemoryHot(memory2) {
|
|
8225
8416
|
memory2.command("hot").description("List memories actively used but not yet validated (good promotion candidates)").option("--threshold <n>", "minimum read_count to qualify", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8226
|
-
const root =
|
|
8227
|
-
const paths =
|
|
8417
|
+
const root = findProjectRoot20(opts.dir);
|
|
8418
|
+
const paths = resolveHaivePaths17(root);
|
|
8228
8419
|
if (!existsSync39(paths.memoriesDir)) {
|
|
8229
8420
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8230
8421
|
process.exitCode = 1;
|
|
@@ -8253,7 +8444,7 @@ function registerMemoryHot(memory2) {
|
|
|
8253
8444
|
console.log(
|
|
8254
8445
|
`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.bold(fm.status)} ${ui.dim(`reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
8255
8446
|
);
|
|
8256
|
-
console.log(` ${ui.dim(
|
|
8447
|
+
console.log(` ${ui.dim(path23.relative(root, filePath))}`);
|
|
8257
8448
|
}
|
|
8258
8449
|
ui.info(
|
|
8259
8450
|
`${candidates.length} hot \u2014 promote drafts with \`haive memory promote <id>\`, then \`haive memory auto-promote --apply\`.`
|
|
@@ -8262,15 +8453,15 @@ function registerMemoryHot(memory2) {
|
|
|
8262
8453
|
}
|
|
8263
8454
|
|
|
8264
8455
|
// src/commands/memory-tried.ts
|
|
8265
|
-
import { mkdir as
|
|
8456
|
+
import { mkdir as mkdir13, writeFile as writeFile19 } from "fs/promises";
|
|
8266
8457
|
import { existsSync as existsSync40 } from "fs";
|
|
8267
|
-
import
|
|
8458
|
+
import path24 from "path";
|
|
8268
8459
|
import "commander";
|
|
8269
8460
|
import {
|
|
8270
8461
|
buildFrontmatter as buildFrontmatter8,
|
|
8271
|
-
findProjectRoot as
|
|
8462
|
+
findProjectRoot as findProjectRoot21,
|
|
8272
8463
|
memoryFilePath as memoryFilePath8,
|
|
8273
|
-
resolveHaivePaths as
|
|
8464
|
+
resolveHaivePaths as resolveHaivePaths18,
|
|
8274
8465
|
serializeMemory as serializeMemory17
|
|
8275
8466
|
} from "@hiveai/core";
|
|
8276
8467
|
function registerMemoryTried(memory2) {
|
|
@@ -8290,8 +8481,8 @@ function registerMemoryTried(memory2) {
|
|
|
8290
8481
|
--paths packages/cli/src/index.ts
|
|
8291
8482
|
`
|
|
8292
8483
|
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8293
|
-
const root =
|
|
8294
|
-
const paths =
|
|
8484
|
+
const root = findProjectRoot21(opts.dir);
|
|
8485
|
+
const paths = resolveHaivePaths18(root);
|
|
8295
8486
|
if (!existsSync40(paths.haiveDir)) {
|
|
8296
8487
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
8297
8488
|
process.exitCode = 1;
|
|
@@ -8315,14 +8506,14 @@ function registerMemoryTried(memory2) {
|
|
|
8315
8506
|
}
|
|
8316
8507
|
const body = lines.join("\n") + "\n";
|
|
8317
8508
|
const file = memoryFilePath8(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
8318
|
-
await
|
|
8509
|
+
await mkdir13(path24.dirname(file), { recursive: true });
|
|
8319
8510
|
if (existsSync40(file)) {
|
|
8320
8511
|
ui.error(`Memory already exists at ${file}`);
|
|
8321
8512
|
process.exitCode = 1;
|
|
8322
8513
|
return;
|
|
8323
8514
|
}
|
|
8324
8515
|
await writeFile19(file, serializeMemory17({ frontmatter, body }), "utf8");
|
|
8325
|
-
ui.success(`Recorded: ${
|
|
8516
|
+
ui.success(`Recorded: ${path24.relative(root, file)}`);
|
|
8326
8517
|
ui.info(`id=${frontmatter.id} type=attempt status=validated (auto-approved)`);
|
|
8327
8518
|
});
|
|
8328
8519
|
}
|
|
@@ -8333,18 +8524,18 @@ function parseCsv4(value) {
|
|
|
8333
8524
|
|
|
8334
8525
|
// src/commands/memory-pending.ts
|
|
8335
8526
|
import { existsSync as existsSync41 } from "fs";
|
|
8336
|
-
import
|
|
8527
|
+
import path25 from "path";
|
|
8337
8528
|
import "commander";
|
|
8338
8529
|
import {
|
|
8339
|
-
findProjectRoot as
|
|
8530
|
+
findProjectRoot as findProjectRoot22,
|
|
8340
8531
|
getUsage as getUsage14,
|
|
8341
8532
|
loadUsageIndex as loadUsageIndex16,
|
|
8342
|
-
resolveHaivePaths as
|
|
8533
|
+
resolveHaivePaths as resolveHaivePaths19
|
|
8343
8534
|
} from "@hiveai/core";
|
|
8344
8535
|
function registerMemoryPending(memory2) {
|
|
8345
8536
|
memory2.command("pending").description("List 'proposed' memories awaiting review (sorted by reads desc)").option("--scope <scope>", "filter by scope (personal | team | module)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8346
|
-
const root =
|
|
8347
|
-
const paths =
|
|
8537
|
+
const root = findProjectRoot22(opts.dir);
|
|
8538
|
+
const paths = resolveHaivePaths19(root);
|
|
8348
8539
|
if (!existsSync41(paths.memoriesDir)) {
|
|
8349
8540
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8350
8541
|
process.exitCode = 1;
|
|
@@ -8373,7 +8564,7 @@ function registerMemoryPending(memory2) {
|
|
|
8373
8564
|
console.log(
|
|
8374
8565
|
`${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count} rejections=${u.rejected_count}`)}`
|
|
8375
8566
|
);
|
|
8376
|
-
console.log(` ${ui.dim(
|
|
8567
|
+
console.log(` ${ui.dim(path25.relative(root, filePath))}`);
|
|
8377
8568
|
}
|
|
8378
8569
|
ui.info(`${proposed.length} pending`);
|
|
8379
8570
|
});
|
|
@@ -8381,22 +8572,22 @@ function registerMemoryPending(memory2) {
|
|
|
8381
8572
|
|
|
8382
8573
|
// src/commands/memory-query.ts
|
|
8383
8574
|
import { existsSync as existsSync43 } from "fs";
|
|
8384
|
-
import
|
|
8575
|
+
import path26 from "path";
|
|
8385
8576
|
import "commander";
|
|
8386
8577
|
import {
|
|
8387
8578
|
extractSnippet as extractSnippet2,
|
|
8388
|
-
findProjectRoot as
|
|
8579
|
+
findProjectRoot as findProjectRoot23,
|
|
8389
8580
|
literalMatchesAllTokens as literalMatchesAllTokens3,
|
|
8390
8581
|
literalMatchesAnyToken as literalMatchesAnyToken4,
|
|
8391
8582
|
pickSnippetNeedle as pickSnippetNeedle2,
|
|
8392
|
-
resolveHaivePaths as
|
|
8583
|
+
resolveHaivePaths as resolveHaivePaths20,
|
|
8393
8584
|
tokenizeQuery as tokenizeQuery6,
|
|
8394
8585
|
trackReads as trackReads4
|
|
8395
8586
|
} from "@hiveai/core";
|
|
8396
8587
|
function registerMemoryQuery(memory2) {
|
|
8397
8588
|
memory2.command("query <text>").alias("search").description("Search memories by id, tag, or substring (AND, OR fallback). Alias: search").option("-d, --dir <dir>", "project root").option("--limit <n>", "max results", "20").option("--scope <scope>", "personal | team | module").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected)").option("--show-rejected", "include rejected memories (hidden by default)").action(async (text, opts) => {
|
|
8398
|
-
const root =
|
|
8399
|
-
const paths =
|
|
8589
|
+
const root = findProjectRoot23(opts.dir);
|
|
8590
|
+
const paths = resolveHaivePaths20(root);
|
|
8400
8591
|
if (!existsSync43(paths.memoriesDir)) {
|
|
8401
8592
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
8402
8593
|
process.exitCode = 1;
|
|
@@ -8438,7 +8629,7 @@ function registerMemoryQuery(memory2) {
|
|
|
8438
8629
|
const fm = mem.frontmatter;
|
|
8439
8630
|
const statusBadge = ui.statusBadge(fm.status);
|
|
8440
8631
|
console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
|
|
8441
|
-
console.log(` ${ui.dim(
|
|
8632
|
+
console.log(` ${ui.dim(path26.relative(root, filePath))}`);
|
|
8442
8633
|
const snippet = extractSnippet2(mem.body, snippetNeedle);
|
|
8443
8634
|
if (snippet) console.log(` ${snippet}`);
|
|
8444
8635
|
}
|
|
@@ -8459,17 +8650,17 @@ import { writeFile as writeFile20 } from "fs/promises";
|
|
|
8459
8650
|
import { existsSync as existsSync44 } from "fs";
|
|
8460
8651
|
import "commander";
|
|
8461
8652
|
import {
|
|
8462
|
-
findProjectRoot as
|
|
8653
|
+
findProjectRoot as findProjectRoot24,
|
|
8463
8654
|
loadUsageIndex as loadUsageIndex17,
|
|
8464
8655
|
recordRejection as recordRejection2,
|
|
8465
|
-
resolveHaivePaths as
|
|
8656
|
+
resolveHaivePaths as resolveHaivePaths21,
|
|
8466
8657
|
saveUsageIndex as saveUsageIndex3,
|
|
8467
8658
|
serializeMemory as serializeMemory18
|
|
8468
8659
|
} from "@hiveai/core";
|
|
8469
8660
|
function registerMemoryReject(memory2) {
|
|
8470
8661
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8471
|
-
const root =
|
|
8472
|
-
const paths =
|
|
8662
|
+
const root = findProjectRoot24(opts.dir);
|
|
8663
|
+
const paths = resolveHaivePaths21(root);
|
|
8473
8664
|
if (!existsSync44(paths.memoriesDir)) {
|
|
8474
8665
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8475
8666
|
process.exitCode = 1;
|
|
@@ -8508,19 +8699,19 @@ function registerMemoryReject(memory2) {
|
|
|
8508
8699
|
// src/commands/memory-rm.ts
|
|
8509
8700
|
import { existsSync as existsSync45 } from "fs";
|
|
8510
8701
|
import { unlink as unlink3 } from "fs/promises";
|
|
8511
|
-
import
|
|
8512
|
-
import { createInterface } from "readline/promises";
|
|
8702
|
+
import path27 from "path";
|
|
8703
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
8513
8704
|
import "commander";
|
|
8514
8705
|
import {
|
|
8515
|
-
findProjectRoot as
|
|
8706
|
+
findProjectRoot as findProjectRoot25,
|
|
8516
8707
|
loadUsageIndex as loadUsageIndex18,
|
|
8517
|
-
resolveHaivePaths as
|
|
8708
|
+
resolveHaivePaths as resolveHaivePaths22,
|
|
8518
8709
|
saveUsageIndex as saveUsageIndex4
|
|
8519
8710
|
} from "@hiveai/core";
|
|
8520
8711
|
function registerMemoryRm(memory2) {
|
|
8521
8712
|
memory2.command("rm <id>").description("Delete a memory file (and its usage entry by default)").option("-y, --yes", "skip the confirmation prompt").option("--keep-usage", "do not remove the usage.json entry").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8522
|
-
const root =
|
|
8523
|
-
const paths =
|
|
8713
|
+
const root = findProjectRoot25(opts.dir);
|
|
8714
|
+
const paths = resolveHaivePaths22(root);
|
|
8524
8715
|
if (!existsSync45(paths.memoriesDir)) {
|
|
8525
8716
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8526
8717
|
process.exitCode = 1;
|
|
@@ -8533,9 +8724,9 @@ function registerMemoryRm(memory2) {
|
|
|
8533
8724
|
process.exitCode = 1;
|
|
8534
8725
|
return;
|
|
8535
8726
|
}
|
|
8536
|
-
const rel =
|
|
8727
|
+
const rel = path27.relative(root, found.filePath);
|
|
8537
8728
|
if (!opts.yes) {
|
|
8538
|
-
const rl =
|
|
8729
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8539
8730
|
const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
|
|
8540
8731
|
rl.close();
|
|
8541
8732
|
if (answer !== "y" && answer !== "yes") {
|
|
@@ -8559,19 +8750,19 @@ function registerMemoryRm(memory2) {
|
|
|
8559
8750
|
// src/commands/memory-show.ts
|
|
8560
8751
|
import { existsSync as existsSync46 } from "fs";
|
|
8561
8752
|
import { readFile as readFile11 } from "fs/promises";
|
|
8562
|
-
import
|
|
8753
|
+
import path28 from "path";
|
|
8563
8754
|
import "commander";
|
|
8564
8755
|
import {
|
|
8565
8756
|
deriveConfidence as deriveConfidence10,
|
|
8566
|
-
findProjectRoot as
|
|
8757
|
+
findProjectRoot as findProjectRoot26,
|
|
8567
8758
|
getUsage as getUsage15,
|
|
8568
8759
|
loadUsageIndex as loadUsageIndex19,
|
|
8569
|
-
resolveHaivePaths as
|
|
8760
|
+
resolveHaivePaths as resolveHaivePaths23
|
|
8570
8761
|
} from "@hiveai/core";
|
|
8571
8762
|
function registerMemoryShow(memory2) {
|
|
8572
8763
|
memory2.command("show <id>").description("Print a memory's frontmatter, body, and confidence/usage").option("--raw", "print the raw file contents instead of a summary").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
8573
|
-
const root =
|
|
8574
|
-
const paths =
|
|
8764
|
+
const root = findProjectRoot26(opts.dir);
|
|
8765
|
+
const paths = resolveHaivePaths23(root);
|
|
8575
8766
|
if (!existsSync46(paths.memoriesDir)) {
|
|
8576
8767
|
ui.error(`No .ai/memories at ${root}.`);
|
|
8577
8768
|
process.exitCode = 1;
|
|
@@ -8601,7 +8792,7 @@ function registerMemoryShow(memory2) {
|
|
|
8601
8792
|
if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
|
|
8602
8793
|
if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
|
|
8603
8794
|
console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
|
|
8604
|
-
console.log(`${ui.dim("file:")} ${
|
|
8795
|
+
console.log(`${ui.dim("file:")} ${path28.relative(root, found.filePath)}`);
|
|
8605
8796
|
if (fm.anchor.paths.length || fm.anchor.symbols.length) {
|
|
8606
8797
|
console.log(ui.dim("anchor:"));
|
|
8607
8798
|
if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
|
|
@@ -8617,19 +8808,19 @@ function registerMemoryShow(memory2) {
|
|
|
8617
8808
|
|
|
8618
8809
|
// src/commands/memory-stats.ts
|
|
8619
8810
|
import { existsSync as existsSync47 } from "fs";
|
|
8620
|
-
import
|
|
8811
|
+
import path29 from "path";
|
|
8621
8812
|
import "commander";
|
|
8622
8813
|
import {
|
|
8623
8814
|
deriveConfidence as deriveConfidence11,
|
|
8624
|
-
findProjectRoot as
|
|
8815
|
+
findProjectRoot as findProjectRoot27,
|
|
8625
8816
|
getUsage as getUsage16,
|
|
8626
8817
|
loadUsageIndex as loadUsageIndex20,
|
|
8627
|
-
resolveHaivePaths as
|
|
8818
|
+
resolveHaivePaths as resolveHaivePaths24
|
|
8628
8819
|
} from "@hiveai/core";
|
|
8629
8820
|
function registerMemoryStats(memory2) {
|
|
8630
8821
|
memory2.command("stats").description("Show usage stats and confidence levels per memory").option("--id <id>", "show stats for a single memory id").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8631
|
-
const root =
|
|
8632
|
-
const paths =
|
|
8822
|
+
const root = findProjectRoot27(opts.dir);
|
|
8823
|
+
const paths = resolveHaivePaths24(root);
|
|
8633
8824
|
if (!existsSync47(paths.memoriesDir)) {
|
|
8634
8825
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
8635
8826
|
process.exitCode = 1;
|
|
@@ -8655,7 +8846,7 @@ function registerMemoryStats(memory2) {
|
|
|
8655
8846
|
console.log(
|
|
8656
8847
|
` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
|
|
8657
8848
|
);
|
|
8658
|
-
console.log(` ${ui.dim(
|
|
8849
|
+
console.log(` ${ui.dim(path29.relative(root, filePath))}`);
|
|
8659
8850
|
}
|
|
8660
8851
|
});
|
|
8661
8852
|
}
|
|
@@ -8663,11 +8854,11 @@ function registerMemoryStats(memory2) {
|
|
|
8663
8854
|
// src/commands/memory-verify.ts
|
|
8664
8855
|
import { writeFile as writeFile21 } from "fs/promises";
|
|
8665
8856
|
import { existsSync as existsSync48 } from "fs";
|
|
8666
|
-
import
|
|
8857
|
+
import path30 from "path";
|
|
8667
8858
|
import "commander";
|
|
8668
8859
|
import {
|
|
8669
|
-
findProjectRoot as
|
|
8670
|
-
resolveHaivePaths as
|
|
8860
|
+
findProjectRoot as findProjectRoot28,
|
|
8861
|
+
resolveHaivePaths as resolveHaivePaths25,
|
|
8671
8862
|
serializeMemory as serializeMemory19,
|
|
8672
8863
|
verifyAnchor as verifyAnchor3
|
|
8673
8864
|
} from "@hiveai/core";
|
|
@@ -8675,8 +8866,8 @@ function registerMemoryVerify(memory2) {
|
|
|
8675
8866
|
memory2.command("verify").description(
|
|
8676
8867
|
"Check that memory anchor paths still exist in the current codebase.\n\n A memory is 'stale' when its anchored file or symbol was moved, deleted, or renamed.\n Stale memories are shown with a warning in get_briefing and should be updated or deleted.\n\n haive sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'haive memory verify' to your haive-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n haive memory verify # check all, report only\n haive memory verify --update # mark stale/fresh on disk\n haive memory verify --id 2026-04-28-gotcha-x # check one memory\n"
|
|
8677
8868
|
).option("--id <id>", "verify a single memory by id").option("--all", "verify every memory (default if --id is omitted)").option("--update", "write status=stale or status=validated back to disk").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8678
|
-
const root =
|
|
8679
|
-
const paths =
|
|
8869
|
+
const root = findProjectRoot28(opts.dir);
|
|
8870
|
+
const paths = resolveHaivePaths25(root);
|
|
8680
8871
|
if (!existsSync48(paths.memoriesDir)) {
|
|
8681
8872
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
8682
8873
|
process.exitCode = 1;
|
|
@@ -8700,7 +8891,7 @@ function registerMemoryVerify(memory2) {
|
|
|
8700
8891
|
anchorlessIds.push(mem.frontmatter.id);
|
|
8701
8892
|
continue;
|
|
8702
8893
|
}
|
|
8703
|
-
const rel =
|
|
8894
|
+
const rel = path30.relative(root, filePath);
|
|
8704
8895
|
if (result.stale) {
|
|
8705
8896
|
staleCount++;
|
|
8706
8897
|
console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
|
|
@@ -8767,15 +8958,15 @@ import { readFile as readFile12 } from "fs/promises";
|
|
|
8767
8958
|
import { existsSync as existsSync49 } from "fs";
|
|
8768
8959
|
import "commander";
|
|
8769
8960
|
import {
|
|
8770
|
-
findProjectRoot as
|
|
8771
|
-
resolveHaivePaths as
|
|
8961
|
+
findProjectRoot as findProjectRoot29,
|
|
8962
|
+
resolveHaivePaths as resolveHaivePaths26
|
|
8772
8963
|
} from "@hiveai/core";
|
|
8773
8964
|
function registerMemoryImport(memory2) {
|
|
8774
8965
|
memory2.command("import").description(
|
|
8775
8966
|
"Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
|
|
8776
8967
|
).requiredOption("--from <file>", "Markdown/text file to import from").option("--scope <scope>", "personal | team (default: team)", "team").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8777
|
-
const root =
|
|
8778
|
-
const paths =
|
|
8968
|
+
const root = findProjectRoot29(opts.dir);
|
|
8969
|
+
const paths = resolveHaivePaths26(root);
|
|
8779
8970
|
if (!existsSync49(paths.haiveDir)) {
|
|
8780
8971
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
8781
8972
|
process.exitCode = 1;
|
|
@@ -8815,13 +9006,13 @@ function registerMemoryImport(memory2) {
|
|
|
8815
9006
|
|
|
8816
9007
|
// src/commands/memory-import-changelog.ts
|
|
8817
9008
|
import { existsSync as existsSync50 } from "fs";
|
|
8818
|
-
import { readFile as readFile13, mkdir as
|
|
8819
|
-
import
|
|
9009
|
+
import { readFile as readFile13, mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
|
|
9010
|
+
import path31 from "path";
|
|
8820
9011
|
import "commander";
|
|
8821
9012
|
import {
|
|
8822
9013
|
buildFrontmatter as buildFrontmatter9,
|
|
8823
|
-
findProjectRoot as
|
|
8824
|
-
resolveHaivePaths as
|
|
9014
|
+
findProjectRoot as findProjectRoot30,
|
|
9015
|
+
resolveHaivePaths as resolveHaivePaths27,
|
|
8825
9016
|
serializeMemory as serializeMemory20
|
|
8826
9017
|
} from "@hiveai/core";
|
|
8827
9018
|
function parseChangelog(content) {
|
|
@@ -8886,9 +9077,9 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8886
9077
|
"--versions <csv>",
|
|
8887
9078
|
"only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
|
|
8888
9079
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8889
|
-
const root =
|
|
8890
|
-
const paths =
|
|
8891
|
-
const changelogPath =
|
|
9080
|
+
const root = findProjectRoot30(opts.dir);
|
|
9081
|
+
const paths = resolveHaivePaths27(root);
|
|
9082
|
+
const changelogPath = path31.resolve(root, opts.fromChangelog);
|
|
8892
9083
|
if (!existsSync50(changelogPath)) {
|
|
8893
9084
|
ui.error(`CHANGELOG not found: ${changelogPath}`);
|
|
8894
9085
|
process.exitCode = 1;
|
|
@@ -8909,10 +9100,10 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8909
9100
|
entries = entries.filter((e) => requested.includes(e.version));
|
|
8910
9101
|
}
|
|
8911
9102
|
}
|
|
8912
|
-
const pkgName = opts.package ??
|
|
9103
|
+
const pkgName = opts.package ?? path31.basename(path31.dirname(changelogPath));
|
|
8913
9104
|
const scope = opts.scope ?? "team";
|
|
8914
|
-
const teamDir =
|
|
8915
|
-
await
|
|
9105
|
+
const teamDir = path31.join(paths.memoriesDir, scope);
|
|
9106
|
+
await mkdir14(teamDir, { recursive: true });
|
|
8916
9107
|
let saved = 0;
|
|
8917
9108
|
for (const entry of entries) {
|
|
8918
9109
|
const lines = [];
|
|
@@ -8934,7 +9125,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8934
9125
|
lines.push("");
|
|
8935
9126
|
}
|
|
8936
9127
|
lines.push(
|
|
8937
|
-
`**Source:** \`${
|
|
9128
|
+
`**Source:** \`${path31.relative(root, changelogPath)}\`
|
|
8938
9129
|
**Action:** Update all usages of ${pkgName} if they rely on any of the above.`
|
|
8939
9130
|
);
|
|
8940
9131
|
const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
|
|
@@ -8949,11 +9140,11 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
8949
9140
|
pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
|
|
8950
9141
|
`v${entry.version}`
|
|
8951
9142
|
],
|
|
8952
|
-
paths: [
|
|
9143
|
+
paths: [path31.relative(root, changelogPath)],
|
|
8953
9144
|
topic: `changelog-${pkgName}-${entry.version}`
|
|
8954
9145
|
});
|
|
8955
9146
|
await writeFile23(
|
|
8956
|
-
|
|
9147
|
+
path31.join(teamDir, `${fm.id}.md`),
|
|
8957
9148
|
serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
|
|
8958
9149
|
"utf8"
|
|
8959
9150
|
);
|
|
@@ -8978,15 +9169,15 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
8978
9169
|
// src/commands/memory-digest.ts
|
|
8979
9170
|
import { existsSync as existsSync51 } from "fs";
|
|
8980
9171
|
import { writeFile as writeFile24 } from "fs/promises";
|
|
8981
|
-
import
|
|
9172
|
+
import path33 from "path";
|
|
8982
9173
|
import "commander";
|
|
8983
9174
|
import {
|
|
8984
9175
|
deriveConfidence as deriveConfidence12,
|
|
8985
|
-
findProjectRoot as
|
|
9176
|
+
findProjectRoot as findProjectRoot31,
|
|
8986
9177
|
getUsage as getUsage17,
|
|
8987
9178
|
loadMemoriesFromDir as loadMemoriesFromDir26,
|
|
8988
9179
|
loadUsageIndex as loadUsageIndex21,
|
|
8989
|
-
resolveHaivePaths as
|
|
9180
|
+
resolveHaivePaths as resolveHaivePaths28
|
|
8990
9181
|
} from "@hiveai/core";
|
|
8991
9182
|
var CONFIDENCE_EMOJI = {
|
|
8992
9183
|
unverified: "\u2B1C",
|
|
@@ -8999,8 +9190,8 @@ function registerMemoryDigest(program2) {
|
|
|
8999
9190
|
program2.command("digest").description(
|
|
9000
9191
|
"Generate a Markdown review digest of recently added or updated memories.\n\n Groups memories by type, shows confidence, status, read count, and anchor info.\n Each memory has action checkboxes (approve / reject / keep as-is) for peer review.\n\n Use this to do a bulk weekly review of team memories, or share with teammates\n as a pull-request attachment so humans can validate what the AI captured.\n\n Examples:\n haive memory digest # last 7 days, team scope\n haive memory digest --days 30 --scope all # last 30 days, all scopes\n haive memory digest --out review.md # write to file\n"
|
|
9001
9192
|
).option("--days <n>", "look-back window in days (default: 7)", "7").option("--scope <scope>", "personal | team | module | all (default: team)", "team").option("--out <file>", "write digest to a file instead of stdout").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9002
|
-
const root =
|
|
9003
|
-
const paths =
|
|
9193
|
+
const root = findProjectRoot31(opts.dir);
|
|
9194
|
+
const paths = resolveHaivePaths28(root);
|
|
9004
9195
|
if (!existsSync51(paths.memoriesDir)) {
|
|
9005
9196
|
ui.error("No .ai/memories found. Run `haive init` first.");
|
|
9006
9197
|
process.exitCode = 1;
|
|
@@ -9073,7 +9264,7 @@ function registerMemoryDigest(program2) {
|
|
|
9073
9264
|
);
|
|
9074
9265
|
const digest = lines.join("\n");
|
|
9075
9266
|
if (opts.out) {
|
|
9076
|
-
const outPath =
|
|
9267
|
+
const outPath = path33.resolve(process.cwd(), opts.out);
|
|
9077
9268
|
await writeFile24(outPath, digest, "utf8");
|
|
9078
9269
|
ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
|
|
9079
9270
|
} else {
|
|
@@ -9083,20 +9274,20 @@ function registerMemoryDigest(program2) {
|
|
|
9083
9274
|
}
|
|
9084
9275
|
|
|
9085
9276
|
// src/commands/session-end.ts
|
|
9086
|
-
import { writeFile as writeFile25, mkdir as
|
|
9277
|
+
import { writeFile as writeFile25, mkdir as mkdir15, readFile as readFile14, rm as rm2 } from "fs/promises";
|
|
9087
9278
|
import { existsSync as existsSync53 } from "fs";
|
|
9088
|
-
import
|
|
9279
|
+
import path34 from "path";
|
|
9089
9280
|
import "commander";
|
|
9090
9281
|
import {
|
|
9091
9282
|
buildFrontmatter as buildFrontmatter10,
|
|
9092
|
-
findProjectRoot as
|
|
9283
|
+
findProjectRoot as findProjectRoot32,
|
|
9093
9284
|
loadMemoriesFromDir as loadMemoriesFromDir27,
|
|
9094
9285
|
memoryFilePath as memoryFilePath9,
|
|
9095
|
-
resolveHaivePaths as
|
|
9286
|
+
resolveHaivePaths as resolveHaivePaths29,
|
|
9096
9287
|
serializeMemory as serializeMemory21
|
|
9097
9288
|
} from "@hiveai/core";
|
|
9098
9289
|
async function buildAutoRecap(paths) {
|
|
9099
|
-
const obsFile =
|
|
9290
|
+
const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
9100
9291
|
if (!existsSync53(obsFile)) return null;
|
|
9101
9292
|
const raw = await readFile14(obsFile, "utf8").catch(() => "");
|
|
9102
9293
|
if (!raw.trim()) return null;
|
|
@@ -9176,8 +9367,8 @@ function registerSessionEnd(session2) {
|
|
|
9176
9367
|
--next "Add integration tests for webhook signature validation"
|
|
9177
9368
|
`
|
|
9178
9369
|
).option("--goal <text>", "what you were trying to accomplish (1\u20132 sentences)").option("--accomplished <text>", "what was actually done (bullet list recommended)").option("--discoveries <text>", "bugs, surprises, or inconsistencies found during this session").option("--files <csv>", "key files touched, comma-separated (used as anchor for staleness detection)").option("--next <text>", "what should happen next (for the next session or a teammate)").option("--scope <scope>", "personal | team | module (default: personal)", "personal").option("--module <name>", "module name (required when scope=module)").option("--auto", "synthesize the recap from .ai/.cache/observations.jsonl (used by Claude Code SessionEnd hook)").option("--quiet", "suppress non-error output (for hook use)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9179
|
-
const root =
|
|
9180
|
-
const paths =
|
|
9370
|
+
const root = findProjectRoot32(opts.dir);
|
|
9371
|
+
const paths = resolveHaivePaths29(root);
|
|
9181
9372
|
if (!existsSync53(paths.haiveDir)) {
|
|
9182
9373
|
if (opts.auto || opts.quiet) return;
|
|
9183
9374
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
@@ -9210,14 +9401,14 @@ function registerSessionEnd(session2) {
|
|
|
9210
9401
|
});
|
|
9211
9402
|
const topic = recapTopic2(scope, opts.module);
|
|
9212
9403
|
const filesTouched = parseCsv5(resolvedFiles);
|
|
9213
|
-
const missingPaths = filesTouched.filter((p) => !existsSync53(
|
|
9404
|
+
const missingPaths = filesTouched.filter((p) => !existsSync53(path34.resolve(root, p)));
|
|
9214
9405
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
9215
9406
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
9216
9407
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
9217
9408
|
}
|
|
9218
9409
|
const cleanupObservations = async () => {
|
|
9219
9410
|
if (!opts.auto) return;
|
|
9220
|
-
const obsFile =
|
|
9411
|
+
const obsFile = path34.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
9221
9412
|
if (existsSync53(obsFile)) await rm2(obsFile).catch(() => {
|
|
9222
9413
|
});
|
|
9223
9414
|
};
|
|
@@ -9231,6 +9422,7 @@ function registerSessionEnd(session2) {
|
|
|
9231
9422
|
const revisionCount = (fm.revision_count ?? 0) + 1;
|
|
9232
9423
|
const newFrontmatter = {
|
|
9233
9424
|
...fm,
|
|
9425
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9234
9426
|
revision_count: revisionCount,
|
|
9235
9427
|
anchor: {
|
|
9236
9428
|
...fm.anchor,
|
|
@@ -9241,7 +9433,7 @@ function registerSessionEnd(session2) {
|
|
|
9241
9433
|
await cleanupObservations();
|
|
9242
9434
|
if (!opts.quiet) {
|
|
9243
9435
|
ui.success(`Session recap updated (revision #${revisionCount})`);
|
|
9244
|
-
ui.info(`id=${fm.id} file=${
|
|
9436
|
+
ui.info(`id=${fm.id} file=${path34.relative(root, topicMatch.filePath)}`);
|
|
9245
9437
|
ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
|
|
9246
9438
|
}
|
|
9247
9439
|
return;
|
|
@@ -9258,12 +9450,12 @@ function registerSessionEnd(session2) {
|
|
|
9258
9450
|
status: "validated"
|
|
9259
9451
|
});
|
|
9260
9452
|
const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
9261
|
-
await
|
|
9453
|
+
await mkdir15(path34.dirname(file), { recursive: true });
|
|
9262
9454
|
await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
|
|
9263
9455
|
await cleanupObservations();
|
|
9264
9456
|
if (!opts.quiet) {
|
|
9265
9457
|
ui.success(`Session recap created`);
|
|
9266
|
-
ui.info(`id=${frontmatter.id} scope=${scope} file=${
|
|
9458
|
+
ui.info(`id=${frontmatter.id} scope=${scope} file=${path34.relative(root, file)}`);
|
|
9267
9459
|
ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
|
|
9268
9460
|
ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
|
|
9269
9461
|
}
|
|
@@ -9277,13 +9469,13 @@ function parseCsv5(value) {
|
|
|
9277
9469
|
// src/commands/snapshot.ts
|
|
9278
9470
|
import { existsSync as existsSync54 } from "fs";
|
|
9279
9471
|
import { readdir as readdir4 } from "fs/promises";
|
|
9280
|
-
import
|
|
9472
|
+
import path35 from "path";
|
|
9281
9473
|
import "commander";
|
|
9282
9474
|
import {
|
|
9283
9475
|
diffContract,
|
|
9284
|
-
findProjectRoot as
|
|
9476
|
+
findProjectRoot as findProjectRoot33,
|
|
9285
9477
|
loadConfig as loadConfig5,
|
|
9286
|
-
resolveHaivePaths as
|
|
9478
|
+
resolveHaivePaths as resolveHaivePaths30,
|
|
9287
9479
|
snapshotContract
|
|
9288
9480
|
} from "@hiveai/core";
|
|
9289
9481
|
function registerSnapshot(program2) {
|
|
@@ -9308,15 +9500,15 @@ function registerSnapshot(program2) {
|
|
|
9308
9500
|
"--format <format>",
|
|
9309
9501
|
"contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
|
|
9310
9502
|
).option("--diff", "compare the contract against its stored snapshot").option("--list", "list all stored contract snapshots").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9311
|
-
const root =
|
|
9312
|
-
const paths =
|
|
9503
|
+
const root = findProjectRoot33(opts.dir);
|
|
9504
|
+
const paths = resolveHaivePaths30(root);
|
|
9313
9505
|
if (!existsSync54(paths.haiveDir)) {
|
|
9314
9506
|
ui.error("No .ai/ found. Run `haive init` first.");
|
|
9315
9507
|
process.exitCode = 1;
|
|
9316
9508
|
return;
|
|
9317
9509
|
}
|
|
9318
9510
|
if (opts.list) {
|
|
9319
|
-
const contractsDir =
|
|
9511
|
+
const contractsDir = path35.join(paths.haiveDir, "contracts");
|
|
9320
9512
|
if (!existsSync54(contractsDir)) {
|
|
9321
9513
|
console.log(ui.dim("No contract snapshots found."));
|
|
9322
9514
|
return;
|
|
@@ -9372,7 +9564,7 @@ function registerSnapshot(program2) {
|
|
|
9372
9564
|
return;
|
|
9373
9565
|
}
|
|
9374
9566
|
const contractPath = opts.contract;
|
|
9375
|
-
const name = opts.name ??
|
|
9567
|
+
const name = opts.name ?? path35.basename(contractPath, path35.extname(contractPath));
|
|
9376
9568
|
const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
|
|
9377
9569
|
const contract = { name, path: contractPath, format };
|
|
9378
9570
|
try {
|
|
@@ -9427,8 +9619,8 @@ async function runDiff(root, haiveDir, contract) {
|
|
|
9427
9619
|
}
|
|
9428
9620
|
}
|
|
9429
9621
|
function detectFormat(filePath) {
|
|
9430
|
-
const ext =
|
|
9431
|
-
const base =
|
|
9622
|
+
const ext = path35.extname(filePath).toLowerCase();
|
|
9623
|
+
const base = path35.basename(filePath).toLowerCase();
|
|
9432
9624
|
if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
|
|
9433
9625
|
if (base.includes("openapi") || base.includes("swagger")) return "openapi";
|
|
9434
9626
|
if (base.includes("schema") || base.includes("graphql")) return "graphql";
|
|
@@ -9442,15 +9634,15 @@ function detectFormat(filePath) {
|
|
|
9442
9634
|
|
|
9443
9635
|
// src/commands/hub.ts
|
|
9444
9636
|
import { existsSync as existsSync55 } from "fs";
|
|
9445
|
-
import { mkdir as
|
|
9446
|
-
import
|
|
9447
|
-
import { spawnSync as
|
|
9637
|
+
import { mkdir as mkdir16, readFile as readFile15, writeFile as writeFile26, copyFile } from "fs/promises";
|
|
9638
|
+
import path36 from "path";
|
|
9639
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
9448
9640
|
import "commander";
|
|
9449
9641
|
import {
|
|
9450
|
-
findProjectRoot as
|
|
9642
|
+
findProjectRoot as findProjectRoot34,
|
|
9451
9643
|
loadConfig as loadConfig6,
|
|
9452
9644
|
loadMemoriesFromDir as loadMemoriesFromDir28,
|
|
9453
|
-
resolveHaivePaths as
|
|
9645
|
+
resolveHaivePaths as resolveHaivePaths31,
|
|
9454
9646
|
saveConfig as saveConfig2,
|
|
9455
9647
|
serializeMemory as serializeMemory23
|
|
9456
9648
|
} from "@hiveai/core";
|
|
@@ -9462,21 +9654,21 @@ function registerHub(program2) {
|
|
|
9462
9654
|
hub.command("init <hubPath>").description(
|
|
9463
9655
|
"Initialize a new team-knowledge hub repo at <hubPath>.\n\n Creates a git repo with a .ai/ directory structure ready for shared memories.\n\n Example:\n haive hub init ../team-hub\n haive hub init /srv/git/team-knowledge\n"
|
|
9464
9656
|
).action(async (hubPath) => {
|
|
9465
|
-
const absPath =
|
|
9466
|
-
await
|
|
9467
|
-
const gitCheck =
|
|
9657
|
+
const absPath = path36.resolve(hubPath);
|
|
9658
|
+
await mkdir16(absPath, { recursive: true });
|
|
9659
|
+
const gitCheck = spawnSync4("git", ["rev-parse", "--git-dir"], { cwd: absPath });
|
|
9468
9660
|
if (gitCheck.status !== 0) {
|
|
9469
|
-
const init =
|
|
9661
|
+
const init = spawnSync4("git", ["init"], { cwd: absPath, encoding: "utf8" });
|
|
9470
9662
|
if (init.status !== 0) {
|
|
9471
9663
|
ui.error(`git init failed: ${init.stderr}`);
|
|
9472
9664
|
process.exitCode = 1;
|
|
9473
9665
|
return;
|
|
9474
9666
|
}
|
|
9475
9667
|
}
|
|
9476
|
-
const sharedDir =
|
|
9477
|
-
await
|
|
9668
|
+
const sharedDir = path36.join(absPath, ".ai", "memories", "shared");
|
|
9669
|
+
await mkdir16(sharedDir, { recursive: true });
|
|
9478
9670
|
await writeFile26(
|
|
9479
|
-
|
|
9671
|
+
path36.join(absPath, ".ai", "README.md"),
|
|
9480
9672
|
`# hAIve Team Knowledge Hub
|
|
9481
9673
|
|
|
9482
9674
|
This repo is a shared knowledge hub for hAIve.
|
|
@@ -9498,12 +9690,12 @@ haive hub pull # import into a project
|
|
|
9498
9690
|
"utf8"
|
|
9499
9691
|
);
|
|
9500
9692
|
await writeFile26(
|
|
9501
|
-
|
|
9693
|
+
path36.join(absPath, ".gitignore"),
|
|
9502
9694
|
".ai/.cache/\n.ai/memories/personal/\n",
|
|
9503
9695
|
"utf8"
|
|
9504
9696
|
);
|
|
9505
|
-
|
|
9506
|
-
|
|
9697
|
+
spawnSync4("git", ["add", "."], { cwd: absPath });
|
|
9698
|
+
spawnSync4("git", ["commit", "-m", "chore: initialize hAIve team-knowledge hub"], {
|
|
9507
9699
|
cwd: absPath,
|
|
9508
9700
|
encoding: "utf8"
|
|
9509
9701
|
});
|
|
@@ -9513,7 +9705,7 @@ haive hub pull # import into a project
|
|
|
9513
9705
|
`
|
|
9514
9706
|
Next steps:
|
|
9515
9707
|
1. Add hubPath to your project's .ai/haive.config.json:
|
|
9516
|
-
{ "hubPath": "${
|
|
9708
|
+
{ "hubPath": "${path36.relative(process.cwd(), absPath)}" }
|
|
9517
9709
|
2. Run \`haive hub push\` to publish your shared memories
|
|
9518
9710
|
3. Share ${absPath} with teammates (git remote, NFS, etc.)
|
|
9519
9711
|
`
|
|
@@ -9532,8 +9724,8 @@ Next steps:
|
|
|
9532
9724
|
haive hub push --commit --message "feat: add payment API contract memories"
|
|
9533
9725
|
`
|
|
9534
9726
|
).option("-d, --dir <dir>", "project root").option("--commit", "auto-commit to the hub repo after pushing").option("--message <msg>", "commit message for the hub (used with --commit)").action(async (opts) => {
|
|
9535
|
-
const root =
|
|
9536
|
-
const paths =
|
|
9727
|
+
const root = findProjectRoot34(opts.dir);
|
|
9728
|
+
const paths = resolveHaivePaths31(root);
|
|
9537
9729
|
const config = await loadConfig6(paths);
|
|
9538
9730
|
if (!config.hubPath) {
|
|
9539
9731
|
ui.error(
|
|
@@ -9542,15 +9734,15 @@ Next steps:
|
|
|
9542
9734
|
process.exitCode = 1;
|
|
9543
9735
|
return;
|
|
9544
9736
|
}
|
|
9545
|
-
const hubRoot =
|
|
9737
|
+
const hubRoot = path36.resolve(root, config.hubPath);
|
|
9546
9738
|
if (!existsSync55(hubRoot)) {
|
|
9547
9739
|
ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
|
|
9548
9740
|
process.exitCode = 1;
|
|
9549
9741
|
return;
|
|
9550
9742
|
}
|
|
9551
|
-
const projectName =
|
|
9552
|
-
const destDir =
|
|
9553
|
-
await
|
|
9743
|
+
const projectName = path36.basename(root);
|
|
9744
|
+
const destDir = path36.join(hubRoot, ".ai", "memories", "shared", projectName);
|
|
9745
|
+
await mkdir16(destDir, { recursive: true });
|
|
9554
9746
|
const all = await loadMemoriesFromDir28(paths.memoriesDir);
|
|
9555
9747
|
const shared = all.filter(
|
|
9556
9748
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
|
|
@@ -9568,7 +9760,7 @@ Next steps:
|
|
|
9568
9760
|
for (const { memory: memory2 } of shared) {
|
|
9569
9761
|
const fm = memory2.frontmatter;
|
|
9570
9762
|
const fileName = `${fm.id}.md`;
|
|
9571
|
-
const destPath =
|
|
9763
|
+
const destPath = path36.join(destDir, fileName);
|
|
9572
9764
|
await writeFile26(destPath, serializeMemory23(memory2), "utf8");
|
|
9573
9765
|
pushed++;
|
|
9574
9766
|
}
|
|
@@ -9576,10 +9768,10 @@ Next steps:
|
|
|
9576
9768
|
console.log(ui.dim(` Location: ${destDir}`));
|
|
9577
9769
|
if (opts.commit) {
|
|
9578
9770
|
const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
|
|
9579
|
-
|
|
9771
|
+
spawnSync4("git", ["add", path36.join(".ai", "memories", "shared", projectName)], {
|
|
9580
9772
|
cwd: hubRoot
|
|
9581
9773
|
});
|
|
9582
|
-
const commit =
|
|
9774
|
+
const commit = spawnSync4("git", ["commit", "-m", message], {
|
|
9583
9775
|
cwd: hubRoot,
|
|
9584
9776
|
encoding: "utf8"
|
|
9585
9777
|
});
|
|
@@ -9601,8 +9793,8 @@ Next steps:
|
|
|
9601
9793
|
hub.command("pull").description(
|
|
9602
9794
|
"Pull shared memories from the hub into this project.\n\n Imports all memories from hub/.ai/memories/shared/ EXCEPT this project's own.\n Imported memories land in .ai/memories/shared/<source-project-name>/.\n\n Examples:\n haive hub pull\n"
|
|
9603
9795
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9604
|
-
const root =
|
|
9605
|
-
const paths =
|
|
9796
|
+
const root = findProjectRoot34(opts.dir);
|
|
9797
|
+
const paths = resolveHaivePaths31(root);
|
|
9606
9798
|
const config = await loadConfig6(paths);
|
|
9607
9799
|
if (!config.hubPath) {
|
|
9608
9800
|
ui.error(
|
|
@@ -9611,13 +9803,13 @@ Next steps:
|
|
|
9611
9803
|
process.exitCode = 1;
|
|
9612
9804
|
return;
|
|
9613
9805
|
}
|
|
9614
|
-
const hubRoot =
|
|
9615
|
-
const hubSharedDir =
|
|
9806
|
+
const hubRoot = path36.resolve(root, config.hubPath);
|
|
9807
|
+
const hubSharedDir = path36.join(hubRoot, ".ai", "memories", "shared");
|
|
9616
9808
|
if (!existsSync55(hubSharedDir)) {
|
|
9617
9809
|
ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
|
|
9618
9810
|
return;
|
|
9619
9811
|
}
|
|
9620
|
-
const projectName =
|
|
9812
|
+
const projectName = path36.basename(root);
|
|
9621
9813
|
const { readdir: readdir6 } = await import("fs/promises");
|
|
9622
9814
|
const projectDirs = (await readdir6(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
|
|
9623
9815
|
if (projectDirs.length === 0) {
|
|
@@ -9627,16 +9819,16 @@ Next steps:
|
|
|
9627
9819
|
let totalImported = 0;
|
|
9628
9820
|
let totalUpdated = 0;
|
|
9629
9821
|
for (const sourceName of projectDirs) {
|
|
9630
|
-
const sourceDir =
|
|
9631
|
-
const destDir =
|
|
9632
|
-
await
|
|
9822
|
+
const sourceDir = path36.join(hubSharedDir, sourceName);
|
|
9823
|
+
const destDir = path36.join(paths.memoriesDir, "shared", sourceName);
|
|
9824
|
+
await mkdir16(destDir, { recursive: true });
|
|
9633
9825
|
const sourceFiles = (await readdir6(sourceDir)).filter((f) => f.endsWith(".md"));
|
|
9634
9826
|
const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
|
|
9635
9827
|
const existingInDest = await loadDir(destDir);
|
|
9636
9828
|
const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
|
|
9637
9829
|
for (const file of sourceFiles) {
|
|
9638
|
-
const srcPath =
|
|
9639
|
-
const destPath =
|
|
9830
|
+
const srcPath = path36.join(sourceDir, file);
|
|
9831
|
+
const destPath = path36.join(destDir, file);
|
|
9640
9832
|
const fileContent = await readFile15(srcPath, "utf8");
|
|
9641
9833
|
const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
|
|
9642
9834
|
if (!alreadyTagged) {
|
|
@@ -9660,21 +9852,21 @@ Next steps:
|
|
|
9660
9852
|
);
|
|
9661
9853
|
});
|
|
9662
9854
|
hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9663
|
-
const root =
|
|
9664
|
-
const paths =
|
|
9855
|
+
const root = findProjectRoot34(opts.dir);
|
|
9856
|
+
const paths = resolveHaivePaths31(root);
|
|
9665
9857
|
const config = await loadConfig6(paths);
|
|
9666
9858
|
console.log(ui.bold("Hub status"));
|
|
9667
9859
|
console.log(
|
|
9668
9860
|
` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
|
|
9669
9861
|
);
|
|
9670
|
-
const sharedDir =
|
|
9862
|
+
const sharedDir = path36.join(paths.memoriesDir, "shared");
|
|
9671
9863
|
if (existsSync55(sharedDir)) {
|
|
9672
9864
|
const { readdir: readdir6 } = await import("fs/promises");
|
|
9673
9865
|
const sources = (await readdir6(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
9674
9866
|
console.log(`
|
|
9675
9867
|
Imported from ${sources.length} source(s):`);
|
|
9676
9868
|
for (const src of sources) {
|
|
9677
|
-
const files = (await readdir6(
|
|
9869
|
+
const files = (await readdir6(path36.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
|
|
9678
9870
|
console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
|
|
9679
9871
|
}
|
|
9680
9872
|
} else {
|
|
@@ -9698,16 +9890,16 @@ Next steps:
|
|
|
9698
9890
|
// src/commands/stats.ts
|
|
9699
9891
|
import "commander";
|
|
9700
9892
|
import { existsSync as existsSync56 } from "fs";
|
|
9701
|
-
import { mkdir as
|
|
9702
|
-
import
|
|
9893
|
+
import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
|
|
9894
|
+
import path37 from "path";
|
|
9703
9895
|
import {
|
|
9704
9896
|
aggregateUsage,
|
|
9705
|
-
findProjectRoot as
|
|
9897
|
+
findProjectRoot as findProjectRoot35,
|
|
9706
9898
|
loadMemoriesFromDir as loadMemoriesFromDir29,
|
|
9707
9899
|
loadUsageIndex as loadUsageIndex23,
|
|
9708
9900
|
parseSince,
|
|
9709
9901
|
readUsageEvents as readUsageEvents2,
|
|
9710
|
-
resolveHaivePaths as
|
|
9902
|
+
resolveHaivePaths as resolveHaivePaths32,
|
|
9711
9903
|
usageLogSize
|
|
9712
9904
|
} from "@hiveai/core";
|
|
9713
9905
|
function registerStats(program2) {
|
|
@@ -9716,8 +9908,8 @@ function registerStats(program2) {
|
|
|
9716
9908
|
"write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
|
|
9717
9909
|
void 0
|
|
9718
9910
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9719
|
-
const root =
|
|
9720
|
-
const paths =
|
|
9911
|
+
const root = findProjectRoot35(opts.dir);
|
|
9912
|
+
const paths = resolveHaivePaths32(root);
|
|
9721
9913
|
if (opts.exportReport) {
|
|
9722
9914
|
await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
|
|
9723
9915
|
return;
|
|
@@ -9771,7 +9963,7 @@ function registerStats(program2) {
|
|
|
9771
9963
|
});
|
|
9772
9964
|
}
|
|
9773
9965
|
async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
9774
|
-
const outAbs =
|
|
9966
|
+
const outAbs = path37.isAbsolute(outRelative) ? path37.resolve(outRelative) : path37.resolve(root, outRelative);
|
|
9775
9967
|
const size = await usageLogSize(paths);
|
|
9776
9968
|
let events = await readUsageEvents2(paths);
|
|
9777
9969
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
@@ -9806,7 +9998,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
9806
9998
|
ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
|
|
9807
9999
|
events = [];
|
|
9808
10000
|
}
|
|
9809
|
-
await
|
|
10001
|
+
await mkdir17(path37.dirname(outAbs), { recursive: true });
|
|
9810
10002
|
const payload = {
|
|
9811
10003
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9812
10004
|
project_root: root,
|
|
@@ -9870,13 +10062,13 @@ import { performance } from "perf_hooks";
|
|
|
9870
10062
|
import "commander";
|
|
9871
10063
|
import {
|
|
9872
10064
|
estimateTokens as estimateTokens3,
|
|
9873
|
-
findProjectRoot as
|
|
9874
|
-
resolveHaivePaths as
|
|
10065
|
+
findProjectRoot as findProjectRoot36,
|
|
10066
|
+
resolveHaivePaths as resolveHaivePaths33
|
|
9875
10067
|
} from "@hiveai/core";
|
|
9876
10068
|
function registerBench(program2) {
|
|
9877
10069
|
program2.command("bench").description("Self-test the local hAIve setup: runs core MCP tools against this project and reports latency + payload size.").option("-t, --task <task>", "task description for ranking-aware tools", "audit dependencies for security risks").option("--json", "emit JSON instead of a table", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9878
|
-
const root =
|
|
9879
|
-
const paths =
|
|
10070
|
+
const root = findProjectRoot36(opts.dir);
|
|
10071
|
+
const paths = resolveHaivePaths33(root);
|
|
9880
10072
|
const ctx = { paths };
|
|
9881
10073
|
const task = opts.task ?? "audit dependencies for security risks";
|
|
9882
10074
|
const scenarios = [
|
|
@@ -9997,9 +10189,9 @@ function summarize(name, t0, payload, notes) {
|
|
|
9997
10189
|
// src/commands/benchmark.ts
|
|
9998
10190
|
import { existsSync as existsSync57 } from "fs";
|
|
9999
10191
|
import { readdir as readdir5, readFile as readFile16, writeFile as writeFile28 } from "fs/promises";
|
|
10000
|
-
import
|
|
10192
|
+
import path38 from "path";
|
|
10001
10193
|
import "commander";
|
|
10002
|
-
import { estimateTokens as estimateTokens4, findProjectRoot as
|
|
10194
|
+
import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot37 } from "@hiveai/core";
|
|
10003
10195
|
function registerBenchmark(program2) {
|
|
10004
10196
|
const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
|
|
10005
10197
|
benchmark.command("report").description("Summarize BENCHMARK_AGENT_REPORT.md files from a paired hAIve/plain agent benchmark.").option("-d, --dir <dir>", "benchmark root", "benchmarks/agent-benchmark").option("--out <file>", "write a Markdown report").option("--json", "emit JSON", false).action(async (opts) => {
|
|
@@ -10012,9 +10204,9 @@ function registerBenchmark(program2) {
|
|
|
10012
10204
|
}
|
|
10013
10205
|
const markdown = renderMarkdown(root, summary, rows);
|
|
10014
10206
|
if (opts.out) {
|
|
10015
|
-
const outFile =
|
|
10207
|
+
const outFile = path38.isAbsolute(opts.out) ? opts.out : path38.join(root, opts.out);
|
|
10016
10208
|
await writeFile28(outFile, markdown, "utf8");
|
|
10017
|
-
ui.success(`wrote ${
|
|
10209
|
+
ui.success(`wrote ${path38.relative(process.cwd(), outFile)}`);
|
|
10018
10210
|
return;
|
|
10019
10211
|
}
|
|
10020
10212
|
console.log(markdown);
|
|
@@ -10038,9 +10230,9 @@ function registerBenchmark(program2) {
|
|
|
10038
10230
|
}
|
|
10039
10231
|
function resolveBenchmarkRoot(dir) {
|
|
10040
10232
|
const candidate = dir ?? "benchmarks/agent-benchmark";
|
|
10041
|
-
if (
|
|
10042
|
-
const projectRoot =
|
|
10043
|
-
return
|
|
10233
|
+
if (path38.isAbsolute(candidate)) return candidate;
|
|
10234
|
+
const projectRoot = findProjectRoot37(process.cwd());
|
|
10235
|
+
return path38.join(projectRoot, candidate);
|
|
10044
10236
|
}
|
|
10045
10237
|
async function collectRows(root) {
|
|
10046
10238
|
if (!existsSync57(root)) throw new Error(`Benchmark directory not found: ${root}`);
|
|
@@ -10048,8 +10240,8 @@ async function collectRows(root) {
|
|
|
10048
10240
|
const rows = [];
|
|
10049
10241
|
for (const entry of entries) {
|
|
10050
10242
|
if (!entry.isDirectory()) continue;
|
|
10051
|
-
const fixtureDir =
|
|
10052
|
-
const reportFile =
|
|
10243
|
+
const fixtureDir = path38.join(root, entry.name);
|
|
10244
|
+
const reportFile = path38.join(fixtureDir, "BENCHMARK_AGENT_REPORT.md");
|
|
10053
10245
|
if (!existsSync57(reportFile)) continue;
|
|
10054
10246
|
const report = await readFile16(reportFile, "utf8");
|
|
10055
10247
|
rows.push(parseAgentReport(entry.name, report));
|
|
@@ -10140,19 +10332,19 @@ function escapeRegExp(value) {
|
|
|
10140
10332
|
}
|
|
10141
10333
|
|
|
10142
10334
|
// src/commands/memory-suggest.ts
|
|
10143
|
-
import { mkdir as
|
|
10335
|
+
import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
|
|
10144
10336
|
import { existsSync as existsSync58 } from "fs";
|
|
10145
|
-
import
|
|
10337
|
+
import path39 from "path";
|
|
10146
10338
|
import "commander";
|
|
10147
10339
|
import {
|
|
10148
10340
|
aggregateUsage as aggregateUsage2,
|
|
10149
10341
|
buildFrontmatter as buildFrontmatter11,
|
|
10150
|
-
findProjectRoot as
|
|
10342
|
+
findProjectRoot as findProjectRoot38,
|
|
10151
10343
|
loadMemoriesFromDir as loadMemoriesFromDir30,
|
|
10152
10344
|
memoryFilePath as memoryFilePath10,
|
|
10153
10345
|
parseSince as parseSince2,
|
|
10154
10346
|
readUsageEvents as readUsageEvents3,
|
|
10155
|
-
resolveHaivePaths as
|
|
10347
|
+
resolveHaivePaths as resolveHaivePaths34,
|
|
10156
10348
|
serializeMemory as serializeMemory24
|
|
10157
10349
|
} from "@hiveai/core";
|
|
10158
10350
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -10165,8 +10357,8 @@ function registerMemorySuggest(memory2) {
|
|
|
10165
10357
|
memory2.command("suggest").description(
|
|
10166
10358
|
"Suggest memories to create based on recurring search queries in the usage log.\n\n Use --auto-save to draft the top-N suggestions as draft memories. They land\n in personal scope by default with status=draft, ready for you to edit and promote."
|
|
10167
10359
|
).option("--since <window>", "ISO date or relative (e.g. '7d', '24h')", "30d").option("--min <count>", "minimum repeat count to surface a query", "2").option("--top-n <n>", "with --auto-save, draft this many top suggestions", "3").option("--scope <scope>", "with --auto-save, scope of drafted memories (personal | team)", "personal").option("--auto-save", "draft top-N suggestions as draft memories on disk", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10168
|
-
const root =
|
|
10169
|
-
const paths =
|
|
10360
|
+
const root = findProjectRoot38(opts.dir);
|
|
10361
|
+
const paths = resolveHaivePaths34(root);
|
|
10170
10362
|
const events = await readUsageEvents3(paths);
|
|
10171
10363
|
if (events.length === 0) {
|
|
10172
10364
|
if (opts.json) {
|
|
@@ -10235,13 +10427,13 @@ function registerMemorySuggest(memory2) {
|
|
|
10235
10427
|
fm.status = "draft";
|
|
10236
10428
|
const body = renderTemplate(s);
|
|
10237
10429
|
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
10238
|
-
await
|
|
10430
|
+
await mkdir18(path39.dirname(file), { recursive: true });
|
|
10239
10431
|
if (existsSync58(file)) {
|
|
10240
|
-
skipped.push({ query: s.query, reason: `file already exists at ${
|
|
10432
|
+
skipped.push({ query: s.query, reason: `file already exists at ${path39.relative(root, file)}` });
|
|
10241
10433
|
continue;
|
|
10242
10434
|
}
|
|
10243
10435
|
await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
|
|
10244
|
-
created.push({ id: fm.id, file:
|
|
10436
|
+
created.push({ id: fm.id, file: path39.relative(root, file), query: s.query });
|
|
10245
10437
|
}
|
|
10246
10438
|
if (opts.json) {
|
|
10247
10439
|
console.log(JSON.stringify({ created, skipped }, null, 2));
|
|
@@ -10336,14 +10528,14 @@ function truncate2(text, max) {
|
|
|
10336
10528
|
// src/commands/memory-archive.ts
|
|
10337
10529
|
import { existsSync as existsSync59 } from "fs";
|
|
10338
10530
|
import { writeFile as writeFile30 } from "fs/promises";
|
|
10339
|
-
import
|
|
10531
|
+
import path40 from "path";
|
|
10340
10532
|
import "commander";
|
|
10341
10533
|
import {
|
|
10342
|
-
findProjectRoot as
|
|
10534
|
+
findProjectRoot as findProjectRoot39,
|
|
10343
10535
|
getUsage as getUsage18,
|
|
10344
10536
|
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
10345
10537
|
loadUsageIndex as loadUsageIndex24,
|
|
10346
|
-
resolveHaivePaths as
|
|
10538
|
+
resolveHaivePaths as resolveHaivePaths35,
|
|
10347
10539
|
serializeMemory as serializeMemory25
|
|
10348
10540
|
} from "@hiveai/core";
|
|
10349
10541
|
var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
|
|
@@ -10351,8 +10543,8 @@ function registerMemoryArchive(memory2) {
|
|
|
10351
10543
|
memory2.command("archive").description(
|
|
10352
10544
|
"Archive obsolete memories: marks status='deprecated' for memories not read in N days\n whose anchored paths have all disappeared (or have no anchor at all).\n\n Defaults to a DRY RUN \u2014 pass --apply to actually rewrite files.\n Targets `attempt` memories by default since they age the fastest.\n\n Recover later with `haive memory edit <id>` to set status back to validated."
|
|
10353
10545
|
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m')", "180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--apply", "actually rewrite files (default: dry run)", false).option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10354
|
-
const root =
|
|
10355
|
-
const paths =
|
|
10546
|
+
const root = findProjectRoot39(opts.dir);
|
|
10547
|
+
const paths = resolveHaivePaths35(root);
|
|
10356
10548
|
if (!existsSync59(paths.memoriesDir)) {
|
|
10357
10549
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
10358
10550
|
process.exitCode = 1;
|
|
@@ -10374,7 +10566,7 @@ function registerMemoryArchive(memory2) {
|
|
|
10374
10566
|
if (typeFilter && fm.type !== typeFilter) continue;
|
|
10375
10567
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
10376
10568
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
10377
|
-
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(
|
|
10569
|
+
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync59(path40.join(paths.root, p)));
|
|
10378
10570
|
const isAnchorless = !hasAnyAnchor;
|
|
10379
10571
|
if (!isAnchorless && !allPathsGone) continue;
|
|
10380
10572
|
const u = getUsage18(usage, fm.id);
|
|
@@ -10450,27 +10642,27 @@ function parseDays(input) {
|
|
|
10450
10642
|
// src/commands/doctor.ts
|
|
10451
10643
|
import { existsSync as existsSync60 } from "fs";
|
|
10452
10644
|
import { stat } from "fs/promises";
|
|
10453
|
-
import
|
|
10645
|
+
import path41 from "path";
|
|
10454
10646
|
import { execSync as execSync3 } from "child_process";
|
|
10455
10647
|
import "commander";
|
|
10456
10648
|
import {
|
|
10457
10649
|
codeMapPath as codeMapPath2,
|
|
10458
|
-
findProjectRoot as
|
|
10650
|
+
findProjectRoot as findProjectRoot40,
|
|
10459
10651
|
getUsage as getUsage19,
|
|
10460
10652
|
loadCodeMap as loadCodeMap5,
|
|
10461
10653
|
loadConfig as loadConfig7,
|
|
10462
10654
|
loadMemoriesFromDir as loadMemoriesFromDir32,
|
|
10463
10655
|
loadUsageIndex as loadUsageIndex25,
|
|
10464
10656
|
readUsageEvents as readUsageEvents4,
|
|
10465
|
-
resolveHaivePaths as
|
|
10657
|
+
resolveHaivePaths as resolveHaivePaths36
|
|
10466
10658
|
} from "@hiveai/core";
|
|
10467
10659
|
var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
|
|
10468
10660
|
function registerDoctor(program2) {
|
|
10469
10661
|
program2.command("doctor").description(
|
|
10470
10662
|
"Analyze the local hAIve setup and emit actionable recommendations.\n\n Inspects: project-context status, memory health (stale/anchorless/decay/pending),\n code-map freshness, usage log signals (low-hit briefings, repeated empty searches).\n\n Read-only by default. Pass --fix to suggest commands you can copy-paste."
|
|
10471
10663
|
).option("--json", "emit JSON instead of human-readable output", false).option("--fix", "include suggested fix commands in human output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10472
|
-
const root =
|
|
10473
|
-
const paths =
|
|
10664
|
+
const root = findProjectRoot40(opts.dir);
|
|
10665
|
+
const paths = resolveHaivePaths36(root);
|
|
10474
10666
|
const findings = [];
|
|
10475
10667
|
if (!existsSync60(paths.haiveDir)) {
|
|
10476
10668
|
findings.push({
|
|
@@ -10612,7 +10804,7 @@ function registerDoctor(program2) {
|
|
|
10612
10804
|
}
|
|
10613
10805
|
const config = await loadConfig7(paths);
|
|
10614
10806
|
if (config.enforcement?.requireBriefingFirst) {
|
|
10615
|
-
const claudeSettings =
|
|
10807
|
+
const claudeSettings = path41.join(root, ".claude", "settings.local.json");
|
|
10616
10808
|
let hasClaudeEnforcement = false;
|
|
10617
10809
|
if (existsSync60(claudeSettings)) {
|
|
10618
10810
|
try {
|
|
@@ -10646,7 +10838,7 @@ function registerDoctor(program2) {
|
|
|
10646
10838
|
timeout: 3e3,
|
|
10647
10839
|
stdio: ["ignore", "pipe", "ignore"]
|
|
10648
10840
|
}).trim();
|
|
10649
|
-
const cliVersion = "0.9.
|
|
10841
|
+
const cliVersion = "0.9.13";
|
|
10650
10842
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
10651
10843
|
findings.push({
|
|
10652
10844
|
severity: "warn",
|
|
@@ -10698,19 +10890,19 @@ function isSearchTool(name) {
|
|
|
10698
10890
|
import { existsSync as existsSync61 } from "fs";
|
|
10699
10891
|
import "commander";
|
|
10700
10892
|
import {
|
|
10701
|
-
findProjectRoot as
|
|
10893
|
+
findProjectRoot as findProjectRoot41,
|
|
10702
10894
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
10703
10895
|
parseSince as parseSince3,
|
|
10704
10896
|
readUsageEvents as readUsageEvents5,
|
|
10705
|
-
resolveHaivePaths as
|
|
10897
|
+
resolveHaivePaths as resolveHaivePaths37
|
|
10706
10898
|
} from "@hiveai/core";
|
|
10707
10899
|
var MS_PER_MINUTE = 6e4;
|
|
10708
10900
|
function registerPlayback(program2) {
|
|
10709
10901
|
program2.command("playback").description(
|
|
10710
10902
|
"Replay past sessions from the usage log. For each session, show:\n - tool calls (what kind, how many)\n - briefing tasks asked\n - memories that have been created since then (that the session didn't have)\n\n Useful to ask 'would today's haive have helped past me on this task?'"
|
|
10711
10903
|
).option("--since <window>", "limit to events in this window (e.g. '7d')", "30d").option("--session-gap <minutes>", "minutes of inactivity that splits a session", "30").option("--limit <n>", "show at most this many sessions (newest first)", "10").option("--json", "emit JSON instead of human-readable output", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10712
|
-
const root =
|
|
10713
|
-
const paths =
|
|
10904
|
+
const root = findProjectRoot41(opts.dir);
|
|
10905
|
+
const paths = resolveHaivePaths37(root);
|
|
10714
10906
|
const events = await readUsageEvents5(paths);
|
|
10715
10907
|
if (events.length === 0) {
|
|
10716
10908
|
if (opts.json) {
|
|
@@ -10815,8 +11007,8 @@ function truncate3(text, max) {
|
|
|
10815
11007
|
import { spawn as spawn4 } from "child_process";
|
|
10816
11008
|
import "commander";
|
|
10817
11009
|
import {
|
|
10818
|
-
findProjectRoot as
|
|
10819
|
-
resolveHaivePaths as
|
|
11010
|
+
findProjectRoot as findProjectRoot42,
|
|
11011
|
+
resolveHaivePaths as resolveHaivePaths38
|
|
10820
11012
|
} from "@hiveai/core";
|
|
10821
11013
|
function registerPrecommit(program2) {
|
|
10822
11014
|
program2.command("precommit").description(
|
|
@@ -10826,8 +11018,8 @@ function registerPrecommit(program2) {
|
|
|
10826
11018
|
"'any' | 'high-confidence' (default) | 'never' (report only)",
|
|
10827
11019
|
"high-confidence"
|
|
10828
11020
|
).option("--no-semantic", "disable semantic search in anti-patterns matching").option("--json", "emit JSON instead of human-readable output", false).option("--paths <paths...>", "explicit paths to check (skips git diff)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10829
|
-
const root =
|
|
10830
|
-
const paths =
|
|
11021
|
+
const root = findProjectRoot42(opts.dir);
|
|
11022
|
+
const paths = resolveHaivePaths38(root);
|
|
10831
11023
|
const ctx = { paths };
|
|
10832
11024
|
let diff = "";
|
|
10833
11025
|
let touchedPaths = opts.paths ?? [];
|
|
@@ -10858,7 +11050,7 @@ function registerPrecommit(program2) {
|
|
|
10858
11050
|
console.log(ui.bold(`hAIve precommit \u2014 ${touchedPaths.length} file(s)`));
|
|
10859
11051
|
console.log(
|
|
10860
11052
|
ui.dim(
|
|
10861
|
-
` anti-patterns: ${result.summary.anti_patterns} relevant memories: ${result.summary.relevant_memories} stale anchors: ${result.summary.stale_anchors}`
|
|
11053
|
+
` anti-patterns: ${result.summary.anti_patterns} blocking: ${result.summary.blocking_warnings ?? result.summary.anti_patterns} relevant memories: ${result.summary.relevant_memories} stale anchors: ${result.summary.stale_anchors}`
|
|
10862
11054
|
)
|
|
10863
11055
|
);
|
|
10864
11056
|
console.log();
|
|
@@ -10922,9 +11114,9 @@ function runCommand3(cmd, args, cwd) {
|
|
|
10922
11114
|
import { existsSync as existsSync63 } from "fs";
|
|
10923
11115
|
import "commander";
|
|
10924
11116
|
import {
|
|
10925
|
-
findProjectRoot as
|
|
11117
|
+
findProjectRoot as findProjectRoot43,
|
|
10926
11118
|
loadMemoriesFromDir as loadMemoriesFromDir34,
|
|
10927
|
-
resolveHaivePaths as
|
|
11119
|
+
resolveHaivePaths as resolveHaivePaths39
|
|
10928
11120
|
} from "@hiveai/core";
|
|
10929
11121
|
var TYPE_RANK = {
|
|
10930
11122
|
decision: 0,
|
|
@@ -10938,8 +11130,8 @@ function registerWelcome(program2) {
|
|
|
10938
11130
|
program2.command("welcome").description(
|
|
10939
11131
|
"Onboarding checklist: ranks validated/proposed **team** memories by type.\nUse after `haive init` so new devs skim institutional knowledge quickly.\n\n haive welcome\n haive welcome --limit 15\n"
|
|
10940
11132
|
).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10941
|
-
const root =
|
|
10942
|
-
const paths =
|
|
11133
|
+
const root = findProjectRoot43(opts.dir);
|
|
11134
|
+
const paths = resolveHaivePaths39(root);
|
|
10943
11135
|
if (!existsSync63(paths.memoriesDir)) {
|
|
10944
11136
|
ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
|
|
10945
11137
|
process.exitCode = 1;
|
|
@@ -10984,12 +11176,12 @@ function registerWelcome(program2) {
|
|
|
10984
11176
|
import { existsSync as existsSync64 } from "fs";
|
|
10985
11177
|
import "commander";
|
|
10986
11178
|
import {
|
|
10987
|
-
findProjectRoot as
|
|
11179
|
+
findProjectRoot as findProjectRoot44,
|
|
10988
11180
|
loadMemoriesFromDir as loadMemoriesFromDir35,
|
|
10989
|
-
resolveHaivePaths as
|
|
11181
|
+
resolveHaivePaths as resolveHaivePaths40
|
|
10990
11182
|
} from "@hiveai/core";
|
|
10991
11183
|
async function lintMemoriesAsync(root) {
|
|
10992
|
-
const paths =
|
|
11184
|
+
const paths = resolveHaivePaths40(root);
|
|
10993
11185
|
const out = [];
|
|
10994
11186
|
if (!existsSync64(paths.memoriesDir)) return out;
|
|
10995
11187
|
const loaded = await loadMemoriesFromDir35(paths.memoriesDir);
|
|
@@ -11052,7 +11244,7 @@ function registerMemoryLint(parent) {
|
|
|
11052
11244
|
parent.command("lint").description(
|
|
11053
11245
|
"Heuristic corpus checks (anchors on key types, headings, verbosity). Static analysis only."
|
|
11054
11246
|
).option("--json", "emit findings as JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11055
|
-
const root =
|
|
11247
|
+
const root = findProjectRoot44(opts.dir);
|
|
11056
11248
|
const findings = await lintMemoriesAsync(root);
|
|
11057
11249
|
if (opts.json) {
|
|
11058
11250
|
console.log(JSON.stringify({ findings_count: findings.length, findings }, null, 2));
|
|
@@ -11098,27 +11290,27 @@ function registerMemorySuggestTopic(memory2) {
|
|
|
11098
11290
|
}
|
|
11099
11291
|
|
|
11100
11292
|
// src/commands/resolve-project.ts
|
|
11101
|
-
import
|
|
11293
|
+
import path43 from "path";
|
|
11102
11294
|
import "commander";
|
|
11103
11295
|
import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
|
|
11104
11296
|
function registerResolveProject(program2) {
|
|
11105
11297
|
program2.command("resolve-project").description(
|
|
11106
11298
|
"Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
|
|
11107
11299
|
).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
|
|
11108
|
-
const info = resolveProjectInfo2({ cwd:
|
|
11300
|
+
const info = resolveProjectInfo2({ cwd: path43.resolve(opts.dir) });
|
|
11109
11301
|
console.log(JSON.stringify({ ok: true, info }, null, 2));
|
|
11110
11302
|
});
|
|
11111
11303
|
}
|
|
11112
11304
|
|
|
11113
11305
|
// src/commands/runtime-journal.ts
|
|
11114
11306
|
import { existsSync as existsSync65 } from "fs";
|
|
11115
|
-
import
|
|
11307
|
+
import path44 from "path";
|
|
11116
11308
|
import "commander";
|
|
11117
11309
|
import {
|
|
11118
11310
|
appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
|
|
11119
|
-
findProjectRoot as
|
|
11311
|
+
findProjectRoot as findProjectRoot45,
|
|
11120
11312
|
readRuntimeJournalTail as readRuntimeJournalTail2,
|
|
11121
|
-
resolveHaivePaths as
|
|
11313
|
+
resolveHaivePaths as resolveHaivePaths41
|
|
11122
11314
|
} from "@hiveai/core";
|
|
11123
11315
|
function registerRuntime(program2) {
|
|
11124
11316
|
const runtime = program2.command("runtime").description(
|
|
@@ -11126,16 +11318,16 @@ function registerRuntime(program2) {
|
|
|
11126
11318
|
);
|
|
11127
11319
|
const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
|
|
11128
11320
|
journal.command("append").description("Append one JSON line to .ai/.runtime/session-journal.ndjson").argument("<message>", "short text to log").option("-k, --kind <kind>", "note | session_end | mcp", "note").option("-d, --dir <dir>", "project root", process.cwd()).action(async (message, opts) => {
|
|
11129
|
-
const root =
|
|
11130
|
-
const paths =
|
|
11321
|
+
const root = path44.resolve(opts.dir ?? process.cwd());
|
|
11322
|
+
const paths = resolveHaivePaths41(findProjectRoot45(root));
|
|
11131
11323
|
const raw = opts.kind ?? "note";
|
|
11132
11324
|
const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
|
|
11133
11325
|
await appendRuntimeJournalEntry3(paths, { kind, message });
|
|
11134
|
-
ui.success(`Appended to ${
|
|
11326
|
+
ui.success(`Appended to ${path44.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
|
|
11135
11327
|
});
|
|
11136
11328
|
journal.command("tail").description("Print the last N entries from the runtime session journal as JSON").option("-n, --limit <n>", "number of lines", "30").option("-d, --dir <dir>", "project root", process.cwd()).action(async (opts) => {
|
|
11137
|
-
const root =
|
|
11138
|
-
const paths =
|
|
11329
|
+
const root = path44.resolve(opts.dir ?? process.cwd());
|
|
11330
|
+
const paths = resolveHaivePaths41(findProjectRoot45(root));
|
|
11139
11331
|
const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
11140
11332
|
if (!existsSync65(paths.haiveDir)) {
|
|
11141
11333
|
ui.error("No .ai/ \u2014 run `haive init` first.");
|
|
@@ -11153,12 +11345,12 @@ function registerRuntime(program2) {
|
|
|
11153
11345
|
|
|
11154
11346
|
// src/commands/memory-timeline.ts
|
|
11155
11347
|
import { existsSync as existsSync66 } from "fs";
|
|
11156
|
-
import
|
|
11348
|
+
import path45 from "path";
|
|
11157
11349
|
import "commander";
|
|
11158
11350
|
import {
|
|
11159
11351
|
collectTimelineEntries as collectTimelineEntries2,
|
|
11160
|
-
findProjectRoot as
|
|
11161
|
-
resolveHaivePaths as
|
|
11352
|
+
findProjectRoot as findProjectRoot46,
|
|
11353
|
+
resolveHaivePaths as resolveHaivePaths42
|
|
11162
11354
|
} from "@hiveai/core";
|
|
11163
11355
|
function registerMemoryTimeline(memory2) {
|
|
11164
11356
|
memory2.command("timeline").description(
|
|
@@ -11169,8 +11361,8 @@ function registerMemoryTimeline(memory2) {
|
|
|
11169
11361
|
process.exitCode = 1;
|
|
11170
11362
|
return;
|
|
11171
11363
|
}
|
|
11172
|
-
const root =
|
|
11173
|
-
const paths =
|
|
11364
|
+
const root = path45.resolve(opts.dir ?? process.cwd());
|
|
11365
|
+
const paths = resolveHaivePaths42(findProjectRoot46(root));
|
|
11174
11366
|
if (!existsSync66(paths.memoriesDir)) {
|
|
11175
11367
|
ui.error("No memories \u2014 run `haive init`.");
|
|
11176
11368
|
process.exitCode = 1;
|
|
@@ -11190,13 +11382,13 @@ function registerMemoryTimeline(memory2) {
|
|
|
11190
11382
|
|
|
11191
11383
|
// src/commands/memory-conflict-candidates.ts
|
|
11192
11384
|
import { existsSync as existsSync67 } from "fs";
|
|
11193
|
-
import
|
|
11385
|
+
import path46 from "path";
|
|
11194
11386
|
import "commander";
|
|
11195
11387
|
import {
|
|
11196
11388
|
findLexicalConflictPairs as findLexicalConflictPairs2,
|
|
11197
11389
|
findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
|
|
11198
|
-
findProjectRoot as
|
|
11199
|
-
resolveHaivePaths as
|
|
11390
|
+
findProjectRoot as findProjectRoot47,
|
|
11391
|
+
resolveHaivePaths as resolveHaivePaths43
|
|
11200
11392
|
} from "@hiveai/core";
|
|
11201
11393
|
function parseTypes(csv) {
|
|
11202
11394
|
const allowed = ["decision", "architecture", "convention", "gotcha"];
|
|
@@ -11212,8 +11404,8 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
11212
11404
|
"decision,architecture,convention,gotcha (lexical scan)",
|
|
11213
11405
|
"decision,architecture"
|
|
11214
11406
|
).option("--min-jaccard <x>", "minimum Jaccard for lexical pairs", "0.45").option("--max-pairs <n>", "cap lexical pairs", "20").option("--max-scan <n>", "max memories scanned (lexical)", "500").option("--max-topic-pairs <n>", "cap topic/status pairs", "20").action(async (opts) => {
|
|
11215
|
-
const root =
|
|
11216
|
-
const paths =
|
|
11407
|
+
const root = path46.resolve(opts.dir ?? process.cwd());
|
|
11408
|
+
const paths = resolveHaivePaths43(findProjectRoot47(root));
|
|
11217
11409
|
if (!existsSync67(paths.memoriesDir)) {
|
|
11218
11410
|
ui.error("No memories \u2014 run `haive init`.");
|
|
11219
11411
|
process.exitCode = 1;
|
|
@@ -11251,11 +11443,11 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
11251
11443
|
// src/commands/enforce.ts
|
|
11252
11444
|
import { spawn as spawn5 } from "child_process";
|
|
11253
11445
|
import { existsSync as existsSync68 } from "fs";
|
|
11254
|
-
import { chmod as chmod2, mkdir as
|
|
11255
|
-
import
|
|
11446
|
+
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile17, rm as rm3, writeFile as writeFile31 } from "fs/promises";
|
|
11447
|
+
import path47 from "path";
|
|
11256
11448
|
import "commander";
|
|
11257
11449
|
import {
|
|
11258
|
-
findProjectRoot as
|
|
11450
|
+
findProjectRoot as findProjectRoot48,
|
|
11259
11451
|
hasRecentBriefingMarker,
|
|
11260
11452
|
isFreshIsoDate,
|
|
11261
11453
|
loadConfig as loadConfig8,
|
|
@@ -11263,7 +11455,7 @@ import {
|
|
|
11263
11455
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
11264
11456
|
readRecentBriefingMarker,
|
|
11265
11457
|
resolveBriefingBudget as resolveBriefingBudget3,
|
|
11266
|
-
resolveHaivePaths as
|
|
11458
|
+
resolveHaivePaths as resolveHaivePaths44,
|
|
11267
11459
|
saveConfig as saveConfig3,
|
|
11268
11460
|
SESSION_RECAP_TTL_MS,
|
|
11269
11461
|
verifyAnchor as verifyAnchor4,
|
|
@@ -11276,9 +11468,9 @@ function registerEnforce(program2) {
|
|
|
11276
11468
|
"Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
|
|
11277
11469
|
);
|
|
11278
11470
|
enforce.command("install").description("Install hAIve enforcement across MCP config, git hooks, CI template, and supported client hooks.").option("-d, --dir <dir>", "project root").option("--no-git", "skip git pre-commit/pre-push enforcement hooks").option("--no-claude", "skip Claude Code hooks").option("--no-ci", "skip GitHub Actions enforcement workflow").action(async (opts) => {
|
|
11279
|
-
const root =
|
|
11280
|
-
const paths =
|
|
11281
|
-
await
|
|
11471
|
+
const root = findProjectRoot48(opts.dir);
|
|
11472
|
+
const paths = resolveHaivePaths44(root);
|
|
11473
|
+
await mkdir19(paths.haiveDir, { recursive: true });
|
|
11282
11474
|
const current = await loadConfig8(paths);
|
|
11283
11475
|
await saveConfig3(paths, {
|
|
11284
11476
|
...current,
|
|
@@ -11302,7 +11494,7 @@ function registerEnforce(program2) {
|
|
|
11302
11494
|
if (opts.claude !== false) {
|
|
11303
11495
|
try {
|
|
11304
11496
|
const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
|
|
11305
|
-
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${
|
|
11497
|
+
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path47.relative(root, result.settingsPath)})`);
|
|
11306
11498
|
} catch (err) {
|
|
11307
11499
|
ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
|
|
11308
11500
|
}
|
|
@@ -11321,15 +11513,15 @@ function registerEnforce(program2) {
|
|
|
11321
11513
|
if (report.should_block) process.exit(2);
|
|
11322
11514
|
});
|
|
11323
11515
|
enforce.command("cleanup").description("Remove generated hAIve runtime/cache artifacts that should not appear in commits.").option("-d, --dir <dir>", "project root").option("--dry-run", "print what would be removed without deleting", false).action(async (opts) => {
|
|
11324
|
-
const root =
|
|
11325
|
-
const paths =
|
|
11516
|
+
const root = findProjectRoot48(opts.dir);
|
|
11517
|
+
const paths = resolveHaivePaths44(root);
|
|
11326
11518
|
const targets = [
|
|
11327
|
-
|
|
11328
|
-
|
|
11519
|
+
path47.join(paths.haiveDir, ".cache"),
|
|
11520
|
+
path47.join(paths.haiveDir, ".runtime")
|
|
11329
11521
|
];
|
|
11330
11522
|
for (const target of targets) {
|
|
11331
11523
|
if (!existsSync68(target)) continue;
|
|
11332
|
-
const rel =
|
|
11524
|
+
const rel = path47.relative(root, target);
|
|
11333
11525
|
if (opts.dryRun) ui.info(`would remove ${rel}`);
|
|
11334
11526
|
else {
|
|
11335
11527
|
await rm3(target, { recursive: true, force: true });
|
|
@@ -11346,9 +11538,9 @@ function registerEnforce(program2) {
|
|
|
11346
11538
|
const payload = await readHookPayload();
|
|
11347
11539
|
const root = resolveRoot(opts.dir, payload);
|
|
11348
11540
|
if (!root) return;
|
|
11349
|
-
const paths =
|
|
11541
|
+
const paths = resolveHaivePaths44(root);
|
|
11350
11542
|
if (!existsSync68(paths.haiveDir)) return;
|
|
11351
|
-
await
|
|
11543
|
+
await mkdir19(paths.runtimeDir, { recursive: true });
|
|
11352
11544
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
11353
11545
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
|
|
11354
11546
|
const budget = resolveBriefingBudget3("quick", {
|
|
@@ -11408,7 +11600,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
11408
11600
|
const payload = await readHookPayload();
|
|
11409
11601
|
const root = resolveRoot(opts.dir, payload);
|
|
11410
11602
|
if (!root) return;
|
|
11411
|
-
const paths =
|
|
11603
|
+
const paths = resolveHaivePaths44(root);
|
|
11412
11604
|
if (!existsSync68(paths.haiveDir)) return;
|
|
11413
11605
|
if (!isWriteLikeTool(payload)) return;
|
|
11414
11606
|
const ok = await hasRecentBriefingMarker(paths, payload.session_id);
|
|
@@ -11431,8 +11623,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
11431
11623
|
});
|
|
11432
11624
|
}
|
|
11433
11625
|
async function runWithEnforcement(command, args, opts) {
|
|
11434
|
-
const root =
|
|
11435
|
-
const paths =
|
|
11626
|
+
const root = findProjectRoot48(opts.dir);
|
|
11627
|
+
const paths = resolveHaivePaths44(root);
|
|
11436
11628
|
if (!existsSync68(paths.haiveDir)) {
|
|
11437
11629
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
11438
11630
|
process.exit(1);
|
|
@@ -11452,7 +11644,7 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
11452
11644
|
process.exit(2);
|
|
11453
11645
|
}
|
|
11454
11646
|
ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
|
|
11455
|
-
ui.info(`Briefing written to ${
|
|
11647
|
+
ui.info(`Briefing written to ${path47.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
|
|
11456
11648
|
const child = spawn5(command, args, {
|
|
11457
11649
|
cwd: root,
|
|
11458
11650
|
stdio: "inherit",
|
|
@@ -11501,9 +11693,9 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
11501
11693
|
source: "haive-run",
|
|
11502
11694
|
memoryIds: briefing.memories.map((m) => m.id)
|
|
11503
11695
|
});
|
|
11504
|
-
const dir =
|
|
11505
|
-
await
|
|
11506
|
-
const file =
|
|
11696
|
+
const dir = path47.join(paths.runtimeDir, "enforcement", "briefings");
|
|
11697
|
+
await mkdir19(dir, { recursive: true });
|
|
11698
|
+
const file = path47.join(dir, `${sessionId}.md`);
|
|
11507
11699
|
const parts = [
|
|
11508
11700
|
"# hAIve Briefing",
|
|
11509
11701
|
"",
|
|
@@ -11525,8 +11717,8 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
11525
11717
|
return file;
|
|
11526
11718
|
}
|
|
11527
11719
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
11528
|
-
const root =
|
|
11529
|
-
const paths =
|
|
11720
|
+
const root = findProjectRoot48(dir);
|
|
11721
|
+
const paths = resolveHaivePaths44(root);
|
|
11530
11722
|
const initialized = existsSync68(paths.haiveDir);
|
|
11531
11723
|
const config = initialized ? await loadConfig8(paths) : {};
|
|
11532
11724
|
const mode = config.enforcement?.mode ?? "strict";
|
|
@@ -11618,9 +11810,11 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
11618
11810
|
async function hasRecentSessionRecap(paths) {
|
|
11619
11811
|
if (!existsSync68(paths.memoriesDir)) return false;
|
|
11620
11812
|
const all = await loadMemoriesFromDir36(paths.memoriesDir);
|
|
11621
|
-
return all.some(
|
|
11622
|
-
|
|
11623
|
-
|
|
11813
|
+
return all.some(({ memory: memory2 }) => {
|
|
11814
|
+
const fm = memory2.frontmatter;
|
|
11815
|
+
const freshnessDate = fm.verified_at ?? fm.created_at;
|
|
11816
|
+
return fm.type === "session_recap" && fm.status !== "rejected" && isFreshIsoDate(freshnessDate, SESSION_RECAP_TTL_MS);
|
|
11817
|
+
});
|
|
11624
11818
|
}
|
|
11625
11819
|
async function verifyMemoryPolicy(paths, config) {
|
|
11626
11820
|
if (!existsSync68(paths.memoriesDir)) return [];
|
|
@@ -11722,7 +11916,7 @@ async function runPrecommitPolicy(paths) {
|
|
|
11722
11916
|
return [{
|
|
11723
11917
|
severity: "error",
|
|
11724
11918
|
code: "precommit-policy-block",
|
|
11725
|
-
message: `Pre-commit policy matched ${result.summary.anti_patterns} anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
|
|
11919
|
+
message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
|
|
11726
11920
|
fix: "Review the hAIve warnings, then update the code or the relevant memories.",
|
|
11727
11921
|
impact: 45
|
|
11728
11922
|
}];
|
|
@@ -11777,12 +11971,12 @@ function buildScore(findings, threshold = 80) {
|
|
|
11777
11971
|
};
|
|
11778
11972
|
}
|
|
11779
11973
|
async function installGitEnforcement(root) {
|
|
11780
|
-
const hooksDir =
|
|
11781
|
-
if (!existsSync68(
|
|
11974
|
+
const hooksDir = path47.join(root, ".git", "hooks");
|
|
11975
|
+
if (!existsSync68(path47.join(root, ".git"))) {
|
|
11782
11976
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
11783
11977
|
return;
|
|
11784
11978
|
}
|
|
11785
|
-
await
|
|
11979
|
+
await mkdir19(hooksDir, { recursive: true });
|
|
11786
11980
|
const hooks = [
|
|
11787
11981
|
{
|
|
11788
11982
|
name: "pre-commit",
|
|
@@ -11800,7 +11994,7 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
11800
11994
|
}
|
|
11801
11995
|
];
|
|
11802
11996
|
for (const hook of hooks) {
|
|
11803
|
-
const file =
|
|
11997
|
+
const file = path47.join(hooksDir, hook.name);
|
|
11804
11998
|
if (existsSync68(file)) {
|
|
11805
11999
|
const current = await readFile17(file, "utf8").catch(() => "");
|
|
11806
12000
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
@@ -11818,8 +12012,8 @@ ${hook.body}`, "utf8");
|
|
|
11818
12012
|
ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
|
|
11819
12013
|
}
|
|
11820
12014
|
async function installCiEnforcement(root) {
|
|
11821
|
-
const workflowPath =
|
|
11822
|
-
await
|
|
12015
|
+
const workflowPath = path47.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
12016
|
+
await mkdir19(path47.dirname(workflowPath), { recursive: true });
|
|
11823
12017
|
if (existsSync68(workflowPath)) {
|
|
11824
12018
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
11825
12019
|
return;
|
|
@@ -11848,7 +12042,7 @@ jobs:
|
|
|
11848
12042
|
- name: Enforce hAIve policy
|
|
11849
12043
|
run: haive enforce ci
|
|
11850
12044
|
`, "utf8");
|
|
11851
|
-
ui.success(`Created ${
|
|
12045
|
+
ui.success(`Created ${path47.relative(root, workflowPath)}`);
|
|
11852
12046
|
}
|
|
11853
12047
|
function printReport(report, json) {
|
|
11854
12048
|
if (json) {
|
|
@@ -11877,7 +12071,7 @@ async function readHookPayload() {
|
|
|
11877
12071
|
}
|
|
11878
12072
|
function resolveRoot(dir, payload) {
|
|
11879
12073
|
try {
|
|
11880
|
-
return
|
|
12074
|
+
return findProjectRoot48(dir ?? payload.cwd);
|
|
11881
12075
|
} catch {
|
|
11882
12076
|
return null;
|
|
11883
12077
|
}
|
|
@@ -11946,14 +12140,15 @@ function registerRun(program2) {
|
|
|
11946
12140
|
}
|
|
11947
12141
|
|
|
11948
12142
|
// src/index.ts
|
|
11949
|
-
var program = new
|
|
11950
|
-
program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.
|
|
12143
|
+
var program = new Command51();
|
|
12144
|
+
program.name("haive").description("hAIve \u2014 policy enforcement layer for AI coding agents").version("0.9.13");
|
|
11951
12145
|
registerInit(program);
|
|
11952
12146
|
registerWelcome(program);
|
|
11953
12147
|
registerResolveProject(program);
|
|
11954
12148
|
registerRuntime(program);
|
|
11955
12149
|
registerEnforce(program);
|
|
11956
12150
|
registerRun(program);
|
|
12151
|
+
registerAgent(program);
|
|
11957
12152
|
registerMcp(program);
|
|
11958
12153
|
registerBriefing(program);
|
|
11959
12154
|
registerTui(program);
|