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