@hiveai/cli 0.9.27 → 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 +544 -379
- 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";
|
|
@@ -11,6 +11,7 @@ import "commander";
|
|
|
11
11
|
import {
|
|
12
12
|
extractActionsBriefBody,
|
|
13
13
|
findProjectRoot as findProjectRoot2,
|
|
14
|
+
isStackPackSeed,
|
|
14
15
|
literalMatchesAllTokens,
|
|
15
16
|
literalMatchesAnyToken,
|
|
16
17
|
loadCodeMap as loadCodeMap3,
|
|
@@ -198,7 +199,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
|
|
|
198
199
|
if (!f) continue;
|
|
199
200
|
counts.set(f, (counts.get(f) ?? 0) + 1);
|
|
200
201
|
}
|
|
201
|
-
let entries = [...counts.entries()].map(([
|
|
202
|
+
let entries = [...counts.entries()].map(([path51, changes]) => ({ path: path51, changes }));
|
|
202
203
|
const lowerPaths = filePaths.map((p) => p.toLowerCase());
|
|
203
204
|
if (lowerPaths.length > 0) {
|
|
204
205
|
entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
|
|
@@ -1098,6 +1099,7 @@ function classifyCliPriority(item, filePaths, tokens, exactTaskHit, partialTaskH
|
|
|
1098
1099
|
const fm = item.memory.frontmatter;
|
|
1099
1100
|
const anchored = filePaths.length > 0 && memoryMatchesAnchorPaths(item.memory, filePaths);
|
|
1100
1101
|
if (anchored || fm.type === "attempt" && exactTaskHit) return "must_read";
|
|
1102
|
+
if (isStackPackSeed(fm)) return "background";
|
|
1101
1103
|
if (exactTaskHit || partialTaskHit || item.score >= 4 || tokens && fm.tags.some((tag) => tokens.includes(tag))) {
|
|
1102
1104
|
return "useful";
|
|
1103
1105
|
}
|
|
@@ -2018,7 +2020,8 @@ import path9 from "path";
|
|
|
2018
2020
|
import {
|
|
2019
2021
|
buildFrontmatter,
|
|
2020
2022
|
memoryFilePath,
|
|
2021
|
-
serializeMemory as serializeMemory2
|
|
2023
|
+
serializeMemory as serializeMemory2,
|
|
2024
|
+
STACK_PACK_TAG
|
|
2022
2025
|
} from "@hiveai/core";
|
|
2023
2026
|
var PACKS = {
|
|
2024
2027
|
nestjs: [
|
|
@@ -2593,6 +2596,7 @@ new ApolloServer({
|
|
|
2593
2596
|
}
|
|
2594
2597
|
]
|
|
2595
2598
|
};
|
|
2599
|
+
var SEED_FOOTER = (stack) => `> _Seeded by \`haive init\` from the **${stack}** stack pack \u2014 generic guidance, not repo-specific. Anchor it to a real file or replace it with a repo-specific note to raise it above background priority._`;
|
|
2596
2600
|
var SUPPORTED_STACKS = Object.keys(PACKS);
|
|
2597
2601
|
function isValidStack(name) {
|
|
2598
2602
|
return name in PACKS;
|
|
@@ -2634,11 +2638,15 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
2634
2638
|
slug: `${stack}-${mem.slug}`,
|
|
2635
2639
|
scope: "team",
|
|
2636
2640
|
status: "validated",
|
|
2637
|
-
|
|
2641
|
+
// STACK_PACK_TAG marks this as generic seed knowledge so briefing ranking
|
|
2642
|
+
// keeps it at `background` priority until it earns a repo-specific anchor.
|
|
2643
|
+
tags: [...mem.tags, STACK_PACK_TAG]
|
|
2638
2644
|
});
|
|
2639
2645
|
const filePath = memoryFilePath(haivePaths, "team", fm.id);
|
|
2640
2646
|
if (existsSync8(filePath)) continue;
|
|
2641
|
-
const content = serializeMemory2({ frontmatter: fm, body: mem.body
|
|
2647
|
+
const content = serializeMemory2({ frontmatter: fm, body: `${mem.body}
|
|
2648
|
+
|
|
2649
|
+
${SEED_FOOTER(stack)}` });
|
|
2642
2650
|
await mkdir4(path9.dirname(filePath), { recursive: true });
|
|
2643
2651
|
await writeFile5(filePath, content, "utf8");
|
|
2644
2652
|
count++;
|
|
@@ -2669,30 +2677,25 @@ TODO \u2014 domain terms and what they mean here.
|
|
|
2669
2677
|
TODO \u2014 known traps, surprising behavior, things newcomers stub their toes on.
|
|
2670
2678
|
`;
|
|
2671
2679
|
var BRIDGE_BODY = `<!-- hAIve bridge file \u2014 do not edit by hand. -->
|
|
2672
|
-
<!-- This file points your AI tool at the shared hAIve project context. -->
|
|
2673
|
-
|
|
2674
|
-
See \`.ai/project-context.md\` for the full project context.
|
|
2675
|
-
Memories live under \`.ai/memories/\` (personal/team/module).
|
|
2676
2680
|
|
|
2677
|
-
|
|
2681
|
+
This repo uses **hAIve** for shared context. The map:
|
|
2678
2682
|
|
|
2679
|
-
|
|
2683
|
+
- \`.ai/project-context.md\` \u2014 project overview, architecture, conventions.
|
|
2684
|
+
- \`.ai/memories/\` \u2014 decisions, gotchas, conventions, failed attempts (personal/team/module).
|
|
2685
|
+
- The breadcrumbs injected below (if any) are the top current memories.
|
|
2680
2686
|
|
|
2681
|
-
|
|
2682
|
-
The **haive** MCP server must be enabled in your client. Until it is unavailable (misconfiguration report to the developer), you **must** use it for substantive work:
|
|
2687
|
+
## Working through hAIve
|
|
2683
2688
|
|
|
2684
|
-
|
|
2685
|
-
|
|
2689
|
+
1. **Before editing** for a goal, call \`get_briefing\` (task + files/symbols) to load ranked context \u2014 or \`mem_relevant_to\` if project context is already loaded this session.
|
|
2690
|
+
2. **When an approach fails**, call \`mem_tried\` right away so the next agent skips the dead end.
|
|
2691
|
+
3. **Before closing** a substantive session, run the \`post_task\` prompt to capture what was learned.
|
|
2686
2692
|
|
|
2687
|
-
|
|
2693
|
+
If the haive MCP server is not available, tell the developer rather than silently skipping it.
|
|
2688
2694
|
|
|
2689
|
-
|
|
2690
|
-
- **Immediately** when an approach fails (wrong API, wrong pattern, wrong assumption), call \`mem_tried\` \u2014 do not wait until the end of the session.
|
|
2695
|
+
## Safety
|
|
2691
2696
|
|
|
2692
|
-
|
|
2693
|
-
-
|
|
2694
|
-
- **Never modify code autonomously** because of a breaking change detected in another project (dependency version bump, API contract change, removed field). Always ask first.
|
|
2695
|
-
- When in doubt about a cross-repo change: ask, don't act.
|
|
2697
|
+
- If \`get_briefing\` returns \`action_required\`, surface each item to the developer (use its \`developer_message\`) and wait for confirmation before changing code.
|
|
2698
|
+
- Never act autonomously on a cross-repo breaking change (dep bump, contract/API diff) \u2014 ask first.
|
|
2696
2699
|
`;
|
|
2697
2700
|
var CURSOR_HAIVE_RULE_MDC = `---
|
|
2698
2701
|
description: Require hAIve MCP (get_briefing / mem_relevant_to) before substantive repo edits
|
|
@@ -2918,7 +2921,10 @@ function registerInit(program2) {
|
|
|
2918
2921
|
}
|
|
2919
2922
|
if (totalSeeded > 0) {
|
|
2920
2923
|
ui.success(
|
|
2921
|
-
`${totalSeeded}
|
|
2924
|
+
`${totalSeeded} starter memories seeded (generic stack guidance, kept at background priority)`
|
|
2925
|
+
);
|
|
2926
|
+
ui.info(
|
|
2927
|
+
"Anchor them to real files or replace them with repo-specific notes to make them high-signal."
|
|
2922
2928
|
);
|
|
2923
2929
|
}
|
|
2924
2930
|
}
|
|
@@ -3663,6 +3669,7 @@ import {
|
|
|
3663
3669
|
isGlobPath,
|
|
3664
3670
|
isAutoPromoteEligible,
|
|
3665
3671
|
isDecaying,
|
|
3672
|
+
isStackPackSeed as isStackPackSeed2,
|
|
3666
3673
|
literalMatchesAllTokens as literalMatchesAllTokens22,
|
|
3667
3674
|
literalMatchesAnyToken as literalMatchesAnyToken22,
|
|
3668
3675
|
loadCodeMap as loadCodeMap4,
|
|
@@ -3676,7 +3683,8 @@ import {
|
|
|
3676
3683
|
serializeMemory as serializeMemory9,
|
|
3677
3684
|
tokenizeQuery as tokenizeQuery22,
|
|
3678
3685
|
trackReads as trackReads3,
|
|
3679
|
-
truncateToTokens
|
|
3686
|
+
truncateToTokens,
|
|
3687
|
+
writeBriefingMarker as writeBriefingMarker2
|
|
3680
3688
|
} from "@hiveai/core";
|
|
3681
3689
|
import { z as z17 } from "zod";
|
|
3682
3690
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap22, queryCodeMap as queryCodeMap22 } from "@hiveai/core";
|
|
@@ -5427,6 +5435,16 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5427
5435
|
);
|
|
5428
5436
|
}
|
|
5429
5437
|
}
|
|
5438
|
+
if (existsSync18(ctx.paths.haiveDir)) {
|
|
5439
|
+
await writeBriefingMarker2(ctx.paths, {
|
|
5440
|
+
sessionId: process.env.HAIVE_SESSION_ID,
|
|
5441
|
+
...input.task ? { task: input.task } : {},
|
|
5442
|
+
source: "mcp-get-briefing",
|
|
5443
|
+
files: input.files,
|
|
5444
|
+
memoryIds: outputMemories.map((m) => m.id)
|
|
5445
|
+
}).catch(() => {
|
|
5446
|
+
});
|
|
5447
|
+
}
|
|
5430
5448
|
return {
|
|
5431
5449
|
...input.task ? { task: input.task } : {},
|
|
5432
5450
|
search_mode: searchMode,
|
|
@@ -5481,6 +5499,9 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
|
|
|
5481
5499
|
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
5482
5500
|
return "must_read";
|
|
5483
5501
|
}
|
|
5502
|
+
if (isStackPackSeed2(fm)) {
|
|
5503
|
+
return "background";
|
|
5504
|
+
}
|
|
5484
5505
|
if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
5485
5506
|
return "useful";
|
|
5486
5507
|
}
|
|
@@ -5963,6 +5984,8 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
5963
5984
|
confidence: deriveConfidence6(fm, u),
|
|
5964
5985
|
body_preview: body.split("\n").slice(0, 5).join("\n").slice(0, 400),
|
|
5965
5986
|
reasons: [reason],
|
|
5987
|
+
tags: fm.tags ?? [],
|
|
5988
|
+
anchor_paths: fm.anchor?.paths ?? [],
|
|
5966
5989
|
...score !== void 0 ? { semantic_score: score } : {}
|
|
5967
5990
|
});
|
|
5968
5991
|
};
|
|
@@ -6553,8 +6576,45 @@ function fileTypeDowngradeReason(warning, paths) {
|
|
|
6553
6576
|
if (configOnly && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
|
|
6554
6577
|
return "package/config-only change; warning has no anchor on these files and no strong semantic match \u2014 downgraded to info.";
|
|
6555
6578
|
}
|
|
6579
|
+
const touchesBuildFile = paths.some(isPackageOrConfigPath);
|
|
6580
|
+
if (!touchesBuildFile && isBuildScopedWarning(warning) && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
|
|
6581
|
+
return "build/packaging gotcha, but no package/build file changed \u2014 downgraded to info.";
|
|
6582
|
+
}
|
|
6556
6583
|
return null;
|
|
6557
6584
|
}
|
|
6585
|
+
function isBuildScopedWarning(warning) {
|
|
6586
|
+
const tags = warning.tags ?? [];
|
|
6587
|
+
if (tags.some((t) => BUILD_SCOPED_TAGS.has(t.toLowerCase()))) return true;
|
|
6588
|
+
const anchors = warning.anchor_paths ?? [];
|
|
6589
|
+
return anchors.length > 0 && anchors.every(isPackageOrConfigPath);
|
|
6590
|
+
}
|
|
6591
|
+
var BUILD_SCOPED_TAGS = /* @__PURE__ */ new Set([
|
|
6592
|
+
"npm",
|
|
6593
|
+
"pnpm",
|
|
6594
|
+
"yarn",
|
|
6595
|
+
"publish",
|
|
6596
|
+
"install",
|
|
6597
|
+
"packaging",
|
|
6598
|
+
"package",
|
|
6599
|
+
"build",
|
|
6600
|
+
"tsup",
|
|
6601
|
+
"bundler",
|
|
6602
|
+
"monorepo",
|
|
6603
|
+
"workspace",
|
|
6604
|
+
"versioning",
|
|
6605
|
+
"version",
|
|
6606
|
+
"dev-workflow",
|
|
6607
|
+
"hotswap",
|
|
6608
|
+
"ci",
|
|
6609
|
+
"workflow",
|
|
6610
|
+
"release",
|
|
6611
|
+
"changelog",
|
|
6612
|
+
"dependencies",
|
|
6613
|
+
"deps",
|
|
6614
|
+
"dependency",
|
|
6615
|
+
"tooling",
|
|
6616
|
+
"config"
|
|
6617
|
+
]);
|
|
6558
6618
|
function hasStrongSemantic(warning) {
|
|
6559
6619
|
return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
|
|
6560
6620
|
}
|
|
@@ -7130,7 +7190,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7130
7190
|
};
|
|
7131
7191
|
}
|
|
7132
7192
|
var SERVER_NAME = "haive";
|
|
7133
|
-
var SERVER_VERSION = "0.9.
|
|
7193
|
+
var SERVER_VERSION = "0.9.29";
|
|
7134
7194
|
function jsonResult(data) {
|
|
7135
7195
|
return {
|
|
7136
7196
|
content: [
|
|
@@ -8111,6 +8171,7 @@ import {
|
|
|
8111
8171
|
getUsage as getUsage10,
|
|
8112
8172
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
8113
8173
|
isDecaying as isDecaying2,
|
|
8174
|
+
isStackPackSeed as isStackPackSeed3,
|
|
8114
8175
|
loadCodeMap as loadCodeMap5,
|
|
8115
8176
|
loadConfig as loadConfig4,
|
|
8116
8177
|
loadMemoriesFromDir as loadMemoriesFromDir23,
|
|
@@ -8555,12 +8616,18 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8555
8616
|
}
|
|
8556
8617
|
});
|
|
8557
8618
|
}
|
|
8619
|
+
function bridgeSummaryLine(body) {
|
|
8620
|
+
const firstLine = body.split("\n").map((l) => l.replace(/^#+\s*/, "").trim()).find((l) => l.length > 0) ?? "";
|
|
8621
|
+
const oneLine = firstLine.replace(/\s+/g, " ");
|
|
8622
|
+
return oneLine.length > 140 ? oneLine.slice(0, 137) + "\u2026" : oneLine;
|
|
8623
|
+
}
|
|
8558
8624
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
8559
8625
|
if (!existsSync29(memoriesDir)) return;
|
|
8560
8626
|
const all = await loadMemoriesFromDir23(memoriesDir);
|
|
8561
8627
|
const top = all.filter(({ memory: memory2 }) => {
|
|
8562
8628
|
const s = memory2.frontmatter.status;
|
|
8563
8629
|
if (memory2.frontmatter.type === "session_recap") return false;
|
|
8630
|
+
if (isStackPackSeed3(memory2.frontmatter)) return false;
|
|
8564
8631
|
return s === "validated" || s === "proposed";
|
|
8565
8632
|
}).sort((a, b) => {
|
|
8566
8633
|
const score = (m) => {
|
|
@@ -8572,11 +8639,11 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
|
8572
8639
|
const block = top.map((m) => {
|
|
8573
8640
|
const fm = m.memory.frontmatter;
|
|
8574
8641
|
const unverified = fm.status === "proposed" ? " [UNVERIFIED]" : "";
|
|
8575
|
-
return
|
|
8576
|
-
|
|
8577
|
-
}).join("\n\n---\n\n");
|
|
8642
|
+
return `- \`${fm.id}\` (${fm.scope}/${fm.type})${unverified} \u2014 ${bridgeSummaryLine(m.memory.body)}`;
|
|
8643
|
+
}).join("\n");
|
|
8578
8644
|
const injected = `${BRIDGE_START}
|
|
8579
8645
|
<!-- AUTO-GENERATED by haive sync --inject-bridge \u2014 do not edit between these markers -->
|
|
8646
|
+
<!-- Top memories \u2014 call get_briefing / mem_get for the full body. -->
|
|
8580
8647
|
|
|
8581
8648
|
` + block + `
|
|
8582
8649
|
|
|
@@ -9545,21 +9612,118 @@ function parseCsv4(value) {
|
|
|
9545
9612
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
9546
9613
|
}
|
|
9547
9614
|
|
|
9548
|
-
// src/commands/memory-
|
|
9615
|
+
// src/commands/memory-seed.ts
|
|
9616
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9549
9617
|
import { existsSync as existsSync41 } from "fs";
|
|
9550
9618
|
import path27 from "path";
|
|
9551
9619
|
import "commander";
|
|
9552
9620
|
import {
|
|
9553
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,
|
|
9554
9718
|
getUsage as getUsage14,
|
|
9555
9719
|
loadUsageIndex as loadUsageIndex16,
|
|
9556
|
-
resolveHaivePaths as
|
|
9720
|
+
resolveHaivePaths as resolveHaivePaths21
|
|
9557
9721
|
} from "@hiveai/core";
|
|
9558
9722
|
function registerMemoryPending(memory2) {
|
|
9559
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) => {
|
|
9560
|
-
const root =
|
|
9561
|
-
const paths =
|
|
9562
|
-
if (!
|
|
9724
|
+
const root = findProjectRoot24(opts.dir);
|
|
9725
|
+
const paths = resolveHaivePaths21(root);
|
|
9726
|
+
if (!existsSync43(paths.memoriesDir)) {
|
|
9563
9727
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9564
9728
|
process.exitCode = 1;
|
|
9565
9729
|
return;
|
|
@@ -9593,7 +9757,7 @@ function registerMemoryPending(memory2) {
|
|
|
9593
9757
|
console.log(
|
|
9594
9758
|
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
9595
9759
|
);
|
|
9596
|
-
console.log(` ${ui.dim(
|
|
9760
|
+
console.log(` ${ui.dim(path28.relative(root, filePath))}`);
|
|
9597
9761
|
}
|
|
9598
9762
|
if (proposed.length > 0) console.log(ui.dim(` \u2192 haive memory approve <id> or haive memory auto-promote`));
|
|
9599
9763
|
console.log();
|
|
@@ -9608,7 +9772,7 @@ function registerMemoryPending(memory2) {
|
|
|
9608
9772
|
console.log(
|
|
9609
9773
|
` ${ui.bold(fm.id)} ${ui.dim(`${fm.scope}/${fm.type}`)} ${ui.dim(`age=${ageStr} reads=${u.read_count}`)}`
|
|
9610
9774
|
);
|
|
9611
|
-
console.log(` ${ui.dim(
|
|
9775
|
+
console.log(` ${ui.dim(path28.relative(root, filePath))}`);
|
|
9612
9776
|
}
|
|
9613
9777
|
console.log(ui.dim(` \u2192 haive memory approve <id> (activate) | haive memory promote <id> (share with team)`));
|
|
9614
9778
|
}
|
|
@@ -9617,24 +9781,24 @@ function registerMemoryPending(memory2) {
|
|
|
9617
9781
|
}
|
|
9618
9782
|
|
|
9619
9783
|
// src/commands/memory-query.ts
|
|
9620
|
-
import { existsSync as
|
|
9621
|
-
import
|
|
9784
|
+
import { existsSync as existsSync44 } from "fs";
|
|
9785
|
+
import path29 from "path";
|
|
9622
9786
|
import "commander";
|
|
9623
9787
|
import {
|
|
9624
9788
|
extractSnippet as extractSnippet2,
|
|
9625
|
-
findProjectRoot as
|
|
9789
|
+
findProjectRoot as findProjectRoot25,
|
|
9626
9790
|
literalMatchesAllTokens as literalMatchesAllTokens3,
|
|
9627
9791
|
literalMatchesAnyToken as literalMatchesAnyToken4,
|
|
9628
9792
|
pickSnippetNeedle as pickSnippetNeedle2,
|
|
9629
|
-
resolveHaivePaths as
|
|
9793
|
+
resolveHaivePaths as resolveHaivePaths22,
|
|
9630
9794
|
tokenizeQuery as tokenizeQuery6,
|
|
9631
9795
|
trackReads as trackReads4
|
|
9632
9796
|
} from "@hiveai/core";
|
|
9633
9797
|
function registerMemoryQuery(memory2) {
|
|
9634
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) => {
|
|
9635
|
-
const root =
|
|
9636
|
-
const paths =
|
|
9637
|
-
if (!
|
|
9799
|
+
const root = findProjectRoot25(opts.dir);
|
|
9800
|
+
const paths = resolveHaivePaths22(root);
|
|
9801
|
+
if (!existsSync44(paths.memoriesDir)) {
|
|
9638
9802
|
ui.error(`No memories directory at ${paths.memoriesDir}. Run \`haive init\` first.`);
|
|
9639
9803
|
process.exitCode = 1;
|
|
9640
9804
|
return;
|
|
@@ -9675,7 +9839,7 @@ function registerMemoryQuery(memory2) {
|
|
|
9675
9839
|
const fm = mem.frontmatter;
|
|
9676
9840
|
const statusBadge = ui.statusBadge(fm.status);
|
|
9677
9841
|
console.log(`${ui.bold(fm.id)} ${ui.dim(fm.scope)} ${statusBadge}`);
|
|
9678
|
-
console.log(` ${ui.dim(
|
|
9842
|
+
console.log(` ${ui.dim(path29.relative(root, filePath))}`);
|
|
9679
9843
|
const snippet = extractSnippet2(mem.body, snippetNeedle);
|
|
9680
9844
|
if (snippet) console.log(` ${snippet}`);
|
|
9681
9845
|
}
|
|
@@ -9693,21 +9857,21 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
9693
9857
|
|
|
9694
9858
|
// src/commands/memory-reject.ts
|
|
9695
9859
|
import { writeFile as writeFile20 } from "fs/promises";
|
|
9696
|
-
import { existsSync as
|
|
9860
|
+
import { existsSync as existsSync45 } from "fs";
|
|
9697
9861
|
import "commander";
|
|
9698
9862
|
import {
|
|
9699
|
-
findProjectRoot as
|
|
9863
|
+
findProjectRoot as findProjectRoot26,
|
|
9700
9864
|
loadUsageIndex as loadUsageIndex17,
|
|
9701
9865
|
recordRejection as recordRejection2,
|
|
9702
|
-
resolveHaivePaths as
|
|
9866
|
+
resolveHaivePaths as resolveHaivePaths23,
|
|
9703
9867
|
saveUsageIndex as saveUsageIndex3,
|
|
9704
9868
|
serializeMemory as serializeMemory18
|
|
9705
9869
|
} from "@hiveai/core";
|
|
9706
9870
|
function registerMemoryReject(memory2) {
|
|
9707
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) => {
|
|
9708
|
-
const root =
|
|
9709
|
-
const paths =
|
|
9710
|
-
if (!
|
|
9872
|
+
const root = findProjectRoot26(opts.dir);
|
|
9873
|
+
const paths = resolveHaivePaths23(root);
|
|
9874
|
+
if (!existsSync45(paths.memoriesDir)) {
|
|
9711
9875
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9712
9876
|
process.exitCode = 1;
|
|
9713
9877
|
return;
|
|
@@ -9743,22 +9907,22 @@ function registerMemoryReject(memory2) {
|
|
|
9743
9907
|
}
|
|
9744
9908
|
|
|
9745
9909
|
// src/commands/memory-rm.ts
|
|
9746
|
-
import { existsSync as
|
|
9910
|
+
import { existsSync as existsSync46 } from "fs";
|
|
9747
9911
|
import { unlink as unlink3 } from "fs/promises";
|
|
9748
|
-
import
|
|
9912
|
+
import path30 from "path";
|
|
9749
9913
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
9750
9914
|
import "commander";
|
|
9751
9915
|
import {
|
|
9752
|
-
findProjectRoot as
|
|
9916
|
+
findProjectRoot as findProjectRoot27,
|
|
9753
9917
|
loadUsageIndex as loadUsageIndex18,
|
|
9754
|
-
resolveHaivePaths as
|
|
9918
|
+
resolveHaivePaths as resolveHaivePaths24,
|
|
9755
9919
|
saveUsageIndex as saveUsageIndex4
|
|
9756
9920
|
} from "@hiveai/core";
|
|
9757
9921
|
function registerMemoryRm(memory2) {
|
|
9758
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) => {
|
|
9759
|
-
const root =
|
|
9760
|
-
const paths =
|
|
9761
|
-
if (!
|
|
9923
|
+
const root = findProjectRoot27(opts.dir);
|
|
9924
|
+
const paths = resolveHaivePaths24(root);
|
|
9925
|
+
if (!existsSync46(paths.memoriesDir)) {
|
|
9762
9926
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9763
9927
|
process.exitCode = 1;
|
|
9764
9928
|
return;
|
|
@@ -9770,7 +9934,7 @@ function registerMemoryRm(memory2) {
|
|
|
9770
9934
|
process.exitCode = 1;
|
|
9771
9935
|
return;
|
|
9772
9936
|
}
|
|
9773
|
-
const rel =
|
|
9937
|
+
const rel = path30.relative(root, found.filePath);
|
|
9774
9938
|
if (!opts.yes) {
|
|
9775
9939
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
9776
9940
|
const answer = (await rl.question(`Delete ${rel}? [y/N] `)).trim().toLowerCase();
|
|
@@ -9794,22 +9958,22 @@ function registerMemoryRm(memory2) {
|
|
|
9794
9958
|
}
|
|
9795
9959
|
|
|
9796
9960
|
// src/commands/memory-show.ts
|
|
9797
|
-
import { existsSync as
|
|
9798
|
-
import { readFile as
|
|
9799
|
-
import
|
|
9961
|
+
import { existsSync as existsSync47 } from "fs";
|
|
9962
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9963
|
+
import path31 from "path";
|
|
9800
9964
|
import "commander";
|
|
9801
9965
|
import {
|
|
9802
9966
|
deriveConfidence as deriveConfidence10,
|
|
9803
|
-
findProjectRoot as
|
|
9967
|
+
findProjectRoot as findProjectRoot28,
|
|
9804
9968
|
getUsage as getUsage15,
|
|
9805
9969
|
loadUsageIndex as loadUsageIndex19,
|
|
9806
|
-
resolveHaivePaths as
|
|
9970
|
+
resolveHaivePaths as resolveHaivePaths25
|
|
9807
9971
|
} from "@hiveai/core";
|
|
9808
9972
|
function registerMemoryShow(memory2) {
|
|
9809
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) => {
|
|
9810
|
-
const root =
|
|
9811
|
-
const paths =
|
|
9812
|
-
if (!
|
|
9974
|
+
const root = findProjectRoot28(opts.dir);
|
|
9975
|
+
const paths = resolveHaivePaths25(root);
|
|
9976
|
+
if (!existsSync47(paths.memoriesDir)) {
|
|
9813
9977
|
ui.error(`No .ai/memories at ${root}.`);
|
|
9814
9978
|
process.exitCode = 1;
|
|
9815
9979
|
return;
|
|
@@ -9822,7 +9986,7 @@ function registerMemoryShow(memory2) {
|
|
|
9822
9986
|
return;
|
|
9823
9987
|
}
|
|
9824
9988
|
if (opts.raw) {
|
|
9825
|
-
console.log(await
|
|
9989
|
+
console.log(await readFile14(found.filePath, "utf8"));
|
|
9826
9990
|
return;
|
|
9827
9991
|
}
|
|
9828
9992
|
const fm = found.memory.frontmatter;
|
|
@@ -9838,7 +10002,7 @@ function registerMemoryShow(memory2) {
|
|
|
9838
10002
|
if (fm.verified_at) console.log(`${ui.dim("verified:")} ${fm.verified_at}`);
|
|
9839
10003
|
if (fm.stale_reason) console.log(`${ui.dim("stale:")} ${fm.stale_reason}`);
|
|
9840
10004
|
console.log(`${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`);
|
|
9841
|
-
console.log(`${ui.dim("file:")} ${
|
|
10005
|
+
console.log(`${ui.dim("file:")} ${path31.relative(root, found.filePath)}`);
|
|
9842
10006
|
if (fm.anchor.paths.length || fm.anchor.symbols.length) {
|
|
9843
10007
|
console.log(ui.dim("anchor:"));
|
|
9844
10008
|
if (fm.anchor.commit) console.log(` ${ui.dim("commit:")} ${fm.anchor.commit}`);
|
|
@@ -9853,21 +10017,21 @@ function registerMemoryShow(memory2) {
|
|
|
9853
10017
|
}
|
|
9854
10018
|
|
|
9855
10019
|
// src/commands/memory-stats.ts
|
|
9856
|
-
import { existsSync as
|
|
9857
|
-
import
|
|
10020
|
+
import { existsSync as existsSync48 } from "fs";
|
|
10021
|
+
import path33 from "path";
|
|
9858
10022
|
import "commander";
|
|
9859
10023
|
import {
|
|
9860
10024
|
deriveConfidence as deriveConfidence11,
|
|
9861
|
-
findProjectRoot as
|
|
10025
|
+
findProjectRoot as findProjectRoot29,
|
|
9862
10026
|
getUsage as getUsage16,
|
|
9863
10027
|
loadUsageIndex as loadUsageIndex20,
|
|
9864
|
-
resolveHaivePaths as
|
|
10028
|
+
resolveHaivePaths as resolveHaivePaths26
|
|
9865
10029
|
} from "@hiveai/core";
|
|
9866
10030
|
function registerMemoryStats(memory2) {
|
|
9867
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) => {
|
|
9868
|
-
const root =
|
|
9869
|
-
const paths =
|
|
9870
|
-
if (!
|
|
10032
|
+
const root = findProjectRoot29(opts.dir);
|
|
10033
|
+
const paths = resolveHaivePaths26(root);
|
|
10034
|
+
if (!existsSync48(paths.memoriesDir)) {
|
|
9871
10035
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
9872
10036
|
process.exitCode = 1;
|
|
9873
10037
|
return;
|
|
@@ -9892,19 +10056,19 @@ function registerMemoryStats(memory2) {
|
|
|
9892
10056
|
console.log(
|
|
9893
10057
|
` ${ui.dim("status:")} ${fm.status} ${ui.dim("reads:")} ${u.read_count} ${ui.dim("rejections:")} ${u.rejected_count}`
|
|
9894
10058
|
);
|
|
9895
|
-
console.log(` ${ui.dim(
|
|
10059
|
+
console.log(` ${ui.dim(path33.relative(root, filePath))}`);
|
|
9896
10060
|
}
|
|
9897
10061
|
});
|
|
9898
10062
|
}
|
|
9899
10063
|
|
|
9900
10064
|
// src/commands/memory-verify.ts
|
|
9901
10065
|
import { writeFile as writeFile21 } from "fs/promises";
|
|
9902
|
-
import { existsSync as
|
|
9903
|
-
import
|
|
10066
|
+
import { existsSync as existsSync49 } from "fs";
|
|
10067
|
+
import path34 from "path";
|
|
9904
10068
|
import "commander";
|
|
9905
10069
|
import {
|
|
9906
|
-
findProjectRoot as
|
|
9907
|
-
resolveHaivePaths as
|
|
10070
|
+
findProjectRoot as findProjectRoot30,
|
|
10071
|
+
resolveHaivePaths as resolveHaivePaths27,
|
|
9908
10072
|
serializeMemory as serializeMemory19,
|
|
9909
10073
|
verifyAnchor as verifyAnchor3
|
|
9910
10074
|
} from "@hiveai/core";
|
|
@@ -9912,9 +10076,9 @@ function registerMemoryVerify(memory2) {
|
|
|
9912
10076
|
memory2.command("verify").description(
|
|
9913
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"
|
|
9914
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) => {
|
|
9915
|
-
const root =
|
|
9916
|
-
const paths =
|
|
9917
|
-
if (!
|
|
10079
|
+
const root = findProjectRoot30(opts.dir);
|
|
10080
|
+
const paths = resolveHaivePaths27(root);
|
|
10081
|
+
if (!existsSync49(paths.memoriesDir)) {
|
|
9918
10082
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
9919
10083
|
process.exitCode = 1;
|
|
9920
10084
|
return;
|
|
@@ -9937,7 +10101,7 @@ function registerMemoryVerify(memory2) {
|
|
|
9937
10101
|
anchorlessIds.push(mem.frontmatter.id);
|
|
9938
10102
|
continue;
|
|
9939
10103
|
}
|
|
9940
|
-
const rel =
|
|
10104
|
+
const rel = path34.relative(root, filePath);
|
|
9941
10105
|
if (result.stale) {
|
|
9942
10106
|
staleCount++;
|
|
9943
10107
|
console.log(`${ui.bold("STALE")} ${mem.frontmatter.id}`);
|
|
@@ -10000,30 +10164,30 @@ function applyVerification2(mem, result) {
|
|
|
10000
10164
|
}
|
|
10001
10165
|
|
|
10002
10166
|
// src/commands/memory-import.ts
|
|
10003
|
-
import { readFile as
|
|
10004
|
-
import { existsSync as
|
|
10167
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
10168
|
+
import { existsSync as existsSync50 } from "fs";
|
|
10005
10169
|
import "commander";
|
|
10006
10170
|
import {
|
|
10007
|
-
findProjectRoot as
|
|
10008
|
-
resolveHaivePaths as
|
|
10171
|
+
findProjectRoot as findProjectRoot31,
|
|
10172
|
+
resolveHaivePaths as resolveHaivePaths28
|
|
10009
10173
|
} from "@hiveai/core";
|
|
10010
10174
|
function registerMemoryImport(memory2) {
|
|
10011
10175
|
memory2.command("import").description(
|
|
10012
10176
|
"Parse a Markdown file and suggest memories via the import_docs MCP prompt (prints a ready-to-use prompt invocation)"
|
|
10013
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) => {
|
|
10014
|
-
const root =
|
|
10015
|
-
const paths =
|
|
10016
|
-
if (!
|
|
10178
|
+
const root = findProjectRoot31(opts.dir);
|
|
10179
|
+
const paths = resolveHaivePaths28(root);
|
|
10180
|
+
if (!existsSync50(paths.haiveDir)) {
|
|
10017
10181
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
10018
10182
|
process.exitCode = 1;
|
|
10019
10183
|
return;
|
|
10020
10184
|
}
|
|
10021
|
-
if (!
|
|
10185
|
+
if (!existsSync50(opts.from)) {
|
|
10022
10186
|
ui.error(`File not found: ${opts.from}`);
|
|
10023
10187
|
process.exitCode = 1;
|
|
10024
10188
|
return;
|
|
10025
10189
|
}
|
|
10026
|
-
const content = await
|
|
10190
|
+
const content = await readFile15(opts.from, "utf8");
|
|
10027
10191
|
const scope = opts.scope ?? "team";
|
|
10028
10192
|
ui.info(`Preparing import from: ${opts.from} (scope=${scope})`);
|
|
10029
10193
|
ui.info(`Content length: ${content.length} chars`);
|
|
@@ -10051,14 +10215,14 @@ function registerMemoryImport(memory2) {
|
|
|
10051
10215
|
}
|
|
10052
10216
|
|
|
10053
10217
|
// src/commands/memory-import-changelog.ts
|
|
10054
|
-
import { existsSync as
|
|
10055
|
-
import { readFile as
|
|
10056
|
-
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";
|
|
10057
10221
|
import "commander";
|
|
10058
10222
|
import {
|
|
10059
10223
|
buildFrontmatter as buildFrontmatter9,
|
|
10060
|
-
findProjectRoot as
|
|
10061
|
-
resolveHaivePaths as
|
|
10224
|
+
findProjectRoot as findProjectRoot32,
|
|
10225
|
+
resolveHaivePaths as resolveHaivePaths29,
|
|
10062
10226
|
serializeMemory as serializeMemory20
|
|
10063
10227
|
} from "@hiveai/core";
|
|
10064
10228
|
function parseChangelog(content) {
|
|
@@ -10123,15 +10287,15 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
10123
10287
|
"--versions <csv>",
|
|
10124
10288
|
"only import specific versions (comma-separated), or 'latest' for the most recent breaking version"
|
|
10125
10289
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10126
|
-
const root =
|
|
10127
|
-
const paths =
|
|
10128
|
-
const changelogPath =
|
|
10129
|
-
if (!
|
|
10290
|
+
const root = findProjectRoot32(opts.dir);
|
|
10291
|
+
const paths = resolveHaivePaths29(root);
|
|
10292
|
+
const changelogPath = path35.resolve(root, opts.fromChangelog);
|
|
10293
|
+
if (!existsSync51(changelogPath)) {
|
|
10130
10294
|
ui.error(`CHANGELOG not found: ${changelogPath}`);
|
|
10131
10295
|
process.exitCode = 1;
|
|
10132
10296
|
return;
|
|
10133
10297
|
}
|
|
10134
|
-
const content = await
|
|
10298
|
+
const content = await readFile16(changelogPath, "utf8");
|
|
10135
10299
|
let entries = parseChangelog(content);
|
|
10136
10300
|
if (entries.length === 0) {
|
|
10137
10301
|
ui.warn("No breaking changes, deprecations, or removals found in the CHANGELOG.");
|
|
@@ -10146,9 +10310,9 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
10146
10310
|
entries = entries.filter((e) => requested.includes(e.version));
|
|
10147
10311
|
}
|
|
10148
10312
|
}
|
|
10149
|
-
const pkgName = opts.package ??
|
|
10313
|
+
const pkgName = opts.package ?? path35.basename(path35.dirname(changelogPath));
|
|
10150
10314
|
const scope = opts.scope ?? "team";
|
|
10151
|
-
const teamDir =
|
|
10315
|
+
const teamDir = path35.join(paths.memoriesDir, scope);
|
|
10152
10316
|
await mkdir14(teamDir, { recursive: true });
|
|
10153
10317
|
let saved = 0;
|
|
10154
10318
|
for (const entry of entries) {
|
|
@@ -10171,7 +10335,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
10171
10335
|
lines.push("");
|
|
10172
10336
|
}
|
|
10173
10337
|
lines.push(
|
|
10174
|
-
`**Source:** \`${
|
|
10338
|
+
`**Source:** \`${path35.relative(root, changelogPath)}\`
|
|
10175
10339
|
**Action:** Update all usages of ${pkgName} if they rely on any of the above.`
|
|
10176
10340
|
);
|
|
10177
10341
|
const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
|
|
@@ -10186,11 +10350,11 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
10186
10350
|
pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase(),
|
|
10187
10351
|
`v${entry.version}`
|
|
10188
10352
|
],
|
|
10189
|
-
paths: [
|
|
10353
|
+
paths: [path35.relative(root, changelogPath)],
|
|
10190
10354
|
topic: `changelog-${pkgName}-${entry.version}`
|
|
10191
10355
|
});
|
|
10192
10356
|
await writeFile23(
|
|
10193
|
-
|
|
10357
|
+
path35.join(teamDir, `${fm.id}.md`),
|
|
10194
10358
|
serializeMemory20({ frontmatter: fm, body: lines.join("\n") }),
|
|
10195
10359
|
"utf8"
|
|
10196
10360
|
);
|
|
@@ -10213,17 +10377,17 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
10213
10377
|
}
|
|
10214
10378
|
|
|
10215
10379
|
// src/commands/memory-digest.ts
|
|
10216
|
-
import { existsSync as
|
|
10380
|
+
import { existsSync as existsSync53 } from "fs";
|
|
10217
10381
|
import { writeFile as writeFile24 } from "fs/promises";
|
|
10218
|
-
import
|
|
10382
|
+
import path36 from "path";
|
|
10219
10383
|
import "commander";
|
|
10220
10384
|
import {
|
|
10221
10385
|
deriveConfidence as deriveConfidence12,
|
|
10222
|
-
findProjectRoot as
|
|
10386
|
+
findProjectRoot as findProjectRoot33,
|
|
10223
10387
|
getUsage as getUsage17,
|
|
10224
10388
|
loadMemoriesFromDir as loadMemoriesFromDir26,
|
|
10225
10389
|
loadUsageIndex as loadUsageIndex21,
|
|
10226
|
-
resolveHaivePaths as
|
|
10390
|
+
resolveHaivePaths as resolveHaivePaths30
|
|
10227
10391
|
} from "@hiveai/core";
|
|
10228
10392
|
var CONFIDENCE_EMOJI = {
|
|
10229
10393
|
unverified: "\u2B1C",
|
|
@@ -10236,9 +10400,9 @@ function registerMemoryDigest(program2) {
|
|
|
10236
10400
|
program2.command("digest").description(
|
|
10237
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"
|
|
10238
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) => {
|
|
10239
|
-
const root =
|
|
10240
|
-
const paths =
|
|
10241
|
-
if (!
|
|
10403
|
+
const root = findProjectRoot33(opts.dir);
|
|
10404
|
+
const paths = resolveHaivePaths30(root);
|
|
10405
|
+
if (!existsSync53(paths.memoriesDir)) {
|
|
10242
10406
|
ui.error("No .ai/memories found. Run `haive init` first.");
|
|
10243
10407
|
process.exitCode = 1;
|
|
10244
10408
|
return;
|
|
@@ -10310,7 +10474,7 @@ function registerMemoryDigest(program2) {
|
|
|
10310
10474
|
);
|
|
10311
10475
|
const digest = lines.join("\n");
|
|
10312
10476
|
if (opts.out) {
|
|
10313
|
-
const outPath =
|
|
10477
|
+
const outPath = path36.resolve(process.cwd(), opts.out);
|
|
10314
10478
|
await writeFile24(outPath, digest, "utf8");
|
|
10315
10479
|
ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
|
|
10316
10480
|
} else {
|
|
@@ -10320,23 +10484,23 @@ function registerMemoryDigest(program2) {
|
|
|
10320
10484
|
}
|
|
10321
10485
|
|
|
10322
10486
|
// src/commands/session-end.ts
|
|
10323
|
-
import { writeFile as writeFile25, mkdir as mkdir15, readFile as
|
|
10324
|
-
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";
|
|
10325
10489
|
import { spawn as spawn4 } from "child_process";
|
|
10326
|
-
import
|
|
10490
|
+
import path37 from "path";
|
|
10327
10491
|
import "commander";
|
|
10328
10492
|
import {
|
|
10329
10493
|
buildFrontmatter as buildFrontmatter10,
|
|
10330
|
-
findProjectRoot as
|
|
10494
|
+
findProjectRoot as findProjectRoot34,
|
|
10331
10495
|
loadMemoriesFromDir as loadMemoriesFromDir27,
|
|
10332
10496
|
memoryFilePath as memoryFilePath9,
|
|
10333
|
-
resolveHaivePaths as
|
|
10497
|
+
resolveHaivePaths as resolveHaivePaths31,
|
|
10334
10498
|
serializeMemory as serializeMemory21
|
|
10335
10499
|
} from "@hiveai/core";
|
|
10336
10500
|
async function buildAutoRecap(paths) {
|
|
10337
|
-
const obsFile =
|
|
10338
|
-
if (!
|
|
10339
|
-
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(() => "");
|
|
10340
10504
|
if (!raw.trim()) return await buildGitAutoRecap(paths);
|
|
10341
10505
|
const lines = raw.split("\n").filter(Boolean);
|
|
10342
10506
|
const obs = [];
|
|
@@ -10524,9 +10688,9 @@ function registerSessionEnd(session2) {
|
|
|
10524
10688
|
--next "Add integration tests for webhook signature validation"
|
|
10525
10689
|
`
|
|
10526
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) => {
|
|
10527
|
-
const root =
|
|
10528
|
-
const paths =
|
|
10529
|
-
if (!
|
|
10691
|
+
const root = findProjectRoot34(opts.dir);
|
|
10692
|
+
const paths = resolveHaivePaths31(root);
|
|
10693
|
+
if (!existsSync54(paths.haiveDir)) {
|
|
10530
10694
|
if (opts.auto || opts.quiet) return;
|
|
10531
10695
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
10532
10696
|
process.exitCode = 1;
|
|
@@ -10559,18 +10723,18 @@ function registerSessionEnd(session2) {
|
|
|
10559
10723
|
});
|
|
10560
10724
|
const topic = recapTopic2(scope, opts.module);
|
|
10561
10725
|
const filesTouched = parseCsv5(resolvedFiles).map((p) => normalizeAnchorPath(root, p));
|
|
10562
|
-
const missingPaths = filesTouched.filter((p) => !
|
|
10726
|
+
const missingPaths = filesTouched.filter((p) => !existsSync54(path37.resolve(root, p)));
|
|
10563
10727
|
if (missingPaths.length > 0 && !opts.quiet) {
|
|
10564
10728
|
ui.warn(`Anchor path${missingPaths.length > 1 ? "s" : ""} not found in project (will be stale):`);
|
|
10565
10729
|
for (const p of missingPaths) ui.warn(` \u2717 ${p}`);
|
|
10566
10730
|
}
|
|
10567
10731
|
const cleanupObservations = async () => {
|
|
10568
10732
|
if (!opts.auto) return;
|
|
10569
|
-
const obsFile =
|
|
10570
|
-
if (
|
|
10733
|
+
const obsFile = path37.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
10734
|
+
if (existsSync54(obsFile)) await rm2(obsFile).catch(() => {
|
|
10571
10735
|
});
|
|
10572
10736
|
};
|
|
10573
|
-
if (
|
|
10737
|
+
if (existsSync54(paths.memoriesDir)) {
|
|
10574
10738
|
const existing = await loadMemoriesFromDir27(paths.memoriesDir);
|
|
10575
10739
|
const topicMatch = existing.find(
|
|
10576
10740
|
({ memory: memory2 }) => memory2.frontmatter.topic === topic && memory2.frontmatter.scope === scope && (!opts.module || memory2.frontmatter.module === opts.module)
|
|
@@ -10591,7 +10755,7 @@ function registerSessionEnd(session2) {
|
|
|
10591
10755
|
await cleanupObservations();
|
|
10592
10756
|
if (!opts.quiet) {
|
|
10593
10757
|
ui.success(`Session recap updated (revision #${revisionCount})`);
|
|
10594
|
-
ui.info(`id=${fm.id} file=${
|
|
10758
|
+
ui.info(`id=${fm.id} file=${path37.relative(root, topicMatch.filePath)}`);
|
|
10595
10759
|
ui.info("Tip: `haive stats --export-report` generates a usage JSON suitable for dashboards.");
|
|
10596
10760
|
}
|
|
10597
10761
|
return;
|
|
@@ -10608,12 +10772,12 @@ function registerSessionEnd(session2) {
|
|
|
10608
10772
|
status: "validated"
|
|
10609
10773
|
});
|
|
10610
10774
|
const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
10611
|
-
await mkdir15(
|
|
10775
|
+
await mkdir15(path37.dirname(file), { recursive: true });
|
|
10612
10776
|
await writeFile25(file, serializeMemory21({ frontmatter, body }), "utf8");
|
|
10613
10777
|
await cleanupObservations();
|
|
10614
10778
|
if (!opts.quiet) {
|
|
10615
10779
|
ui.success(`Session recap created`);
|
|
10616
|
-
ui.info(`id=${frontmatter.id} scope=${scope} file=${
|
|
10780
|
+
ui.info(`id=${frontmatter.id} scope=${scope} file=${path37.relative(root, file)}`);
|
|
10617
10781
|
ui.info("Next session: call `get_briefing` \u2014 the recap will be surfaced automatically.");
|
|
10618
10782
|
ui.info("Tip: export a local MCP usage rollup with `haive stats --export-report .ai/tool-usage-roi-report.json`.");
|
|
10619
10783
|
}
|
|
@@ -10625,22 +10789,22 @@ function parseCsv5(value) {
|
|
|
10625
10789
|
}
|
|
10626
10790
|
function normalizeAnchorPath(root, filePath) {
|
|
10627
10791
|
if (!filePath) return filePath;
|
|
10628
|
-
if (!
|
|
10629
|
-
const rel =
|
|
10792
|
+
if (!path37.isAbsolute(filePath)) return filePath;
|
|
10793
|
+
const rel = path37.relative(root, filePath);
|
|
10630
10794
|
if (rel.startsWith("..")) return filePath;
|
|
10631
10795
|
return rel;
|
|
10632
10796
|
}
|
|
10633
10797
|
|
|
10634
10798
|
// src/commands/snapshot.ts
|
|
10635
|
-
import { existsSync as
|
|
10799
|
+
import { existsSync as existsSync55 } from "fs";
|
|
10636
10800
|
import { readdir as readdir4 } from "fs/promises";
|
|
10637
|
-
import
|
|
10801
|
+
import path38 from "path";
|
|
10638
10802
|
import "commander";
|
|
10639
10803
|
import {
|
|
10640
10804
|
diffContract,
|
|
10641
|
-
findProjectRoot as
|
|
10642
|
-
loadConfig as
|
|
10643
|
-
resolveHaivePaths as
|
|
10805
|
+
findProjectRoot as findProjectRoot35,
|
|
10806
|
+
loadConfig as loadConfig7,
|
|
10807
|
+
resolveHaivePaths as resolveHaivePaths32,
|
|
10644
10808
|
snapshotContract
|
|
10645
10809
|
} from "@hiveai/core";
|
|
10646
10810
|
function registerSnapshot(program2) {
|
|
@@ -10665,16 +10829,16 @@ function registerSnapshot(program2) {
|
|
|
10665
10829
|
"--format <format>",
|
|
10666
10830
|
"contract format: openapi | graphql | proto | typescript | json-schema (auto-detected if omitted)"
|
|
10667
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) => {
|
|
10668
|
-
const root =
|
|
10669
|
-
const paths =
|
|
10670
|
-
if (!
|
|
10832
|
+
const root = findProjectRoot35(opts.dir);
|
|
10833
|
+
const paths = resolveHaivePaths32(root);
|
|
10834
|
+
if (!existsSync55(paths.haiveDir)) {
|
|
10671
10835
|
ui.error("No .ai/ found. Run `haive init` first.");
|
|
10672
10836
|
process.exitCode = 1;
|
|
10673
10837
|
return;
|
|
10674
10838
|
}
|
|
10675
10839
|
if (opts.list) {
|
|
10676
|
-
const contractsDir =
|
|
10677
|
-
if (!
|
|
10840
|
+
const contractsDir = path38.join(paths.haiveDir, "contracts");
|
|
10841
|
+
if (!existsSync55(contractsDir)) {
|
|
10678
10842
|
console.log(ui.dim("No contract snapshots found."));
|
|
10679
10843
|
return;
|
|
10680
10844
|
}
|
|
@@ -10694,7 +10858,7 @@ function registerSnapshot(program2) {
|
|
|
10694
10858
|
}
|
|
10695
10859
|
if (opts.diff) {
|
|
10696
10860
|
if (!opts.name) {
|
|
10697
|
-
const config2 = await
|
|
10861
|
+
const config2 = await loadConfig7(paths);
|
|
10698
10862
|
const contracts = config2.contractFiles ?? [];
|
|
10699
10863
|
if (contracts.length === 0) {
|
|
10700
10864
|
ui.error("--diff requires --name, or configure contractFiles in haive.config.json");
|
|
@@ -10706,7 +10870,7 @@ function registerSnapshot(program2) {
|
|
|
10706
10870
|
}
|
|
10707
10871
|
return;
|
|
10708
10872
|
}
|
|
10709
|
-
const config = await
|
|
10873
|
+
const config = await loadConfig7(paths);
|
|
10710
10874
|
const configured = (config.contractFiles ?? []).find((c) => c.name === opts.name);
|
|
10711
10875
|
if (!configured && !opts.contract) {
|
|
10712
10876
|
ui.error(
|
|
@@ -10729,7 +10893,7 @@ function registerSnapshot(program2) {
|
|
|
10729
10893
|
return;
|
|
10730
10894
|
}
|
|
10731
10895
|
const contractPath = opts.contract;
|
|
10732
|
-
const name = opts.name ??
|
|
10896
|
+
const name = opts.name ?? path38.basename(contractPath, path38.extname(contractPath));
|
|
10733
10897
|
const format = opts.format ?? detectFormat(contractPath) ?? "openapi";
|
|
10734
10898
|
const contract = { name, path: contractPath, format };
|
|
10735
10899
|
try {
|
|
@@ -10784,8 +10948,8 @@ async function runDiff(root, haiveDir, contract) {
|
|
|
10784
10948
|
}
|
|
10785
10949
|
}
|
|
10786
10950
|
function detectFormat(filePath) {
|
|
10787
|
-
const ext =
|
|
10788
|
-
const base =
|
|
10951
|
+
const ext = path38.extname(filePath).toLowerCase();
|
|
10952
|
+
const base = path38.basename(filePath).toLowerCase();
|
|
10789
10953
|
if (ext === ".yaml" || ext === ".yml" || ext === ".json") {
|
|
10790
10954
|
if (base.includes("openapi") || base.includes("swagger")) return "openapi";
|
|
10791
10955
|
if (base.includes("schema") || base.includes("graphql")) return "graphql";
|
|
@@ -10798,16 +10962,16 @@ function detectFormat(filePath) {
|
|
|
10798
10962
|
}
|
|
10799
10963
|
|
|
10800
10964
|
// src/commands/hub.ts
|
|
10801
|
-
import { existsSync as
|
|
10802
|
-
import { mkdir as mkdir16, readFile as
|
|
10803
|
-
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";
|
|
10804
10968
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
10805
10969
|
import "commander";
|
|
10806
10970
|
import {
|
|
10807
|
-
findProjectRoot as
|
|
10808
|
-
loadConfig as
|
|
10971
|
+
findProjectRoot as findProjectRoot36,
|
|
10972
|
+
loadConfig as loadConfig8,
|
|
10809
10973
|
loadMemoriesFromDir as loadMemoriesFromDir28,
|
|
10810
|
-
resolveHaivePaths as
|
|
10974
|
+
resolveHaivePaths as resolveHaivePaths33,
|
|
10811
10975
|
saveConfig as saveConfig3,
|
|
10812
10976
|
serializeMemory as serializeMemory23
|
|
10813
10977
|
} from "@hiveai/core";
|
|
@@ -10819,7 +10983,7 @@ function registerHub(program2) {
|
|
|
10819
10983
|
hub.command("init <hubPath>").description(
|
|
10820
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"
|
|
10821
10985
|
).action(async (hubPath) => {
|
|
10822
|
-
const absPath =
|
|
10986
|
+
const absPath = path39.resolve(hubPath);
|
|
10823
10987
|
await mkdir16(absPath, { recursive: true });
|
|
10824
10988
|
const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
|
|
10825
10989
|
if (gitCheck.status !== 0) {
|
|
@@ -10830,10 +10994,10 @@ function registerHub(program2) {
|
|
|
10830
10994
|
return;
|
|
10831
10995
|
}
|
|
10832
10996
|
}
|
|
10833
|
-
const sharedDir =
|
|
10997
|
+
const sharedDir = path39.join(absPath, ".ai", "memories", "shared");
|
|
10834
10998
|
await mkdir16(sharedDir, { recursive: true });
|
|
10835
10999
|
await writeFile26(
|
|
10836
|
-
|
|
11000
|
+
path39.join(absPath, ".ai", "README.md"),
|
|
10837
11001
|
`# hAIve Team Knowledge Hub
|
|
10838
11002
|
|
|
10839
11003
|
This repo is a shared knowledge hub for hAIve.
|
|
@@ -10855,7 +11019,7 @@ haive hub pull # import into a project
|
|
|
10855
11019
|
"utf8"
|
|
10856
11020
|
);
|
|
10857
11021
|
await writeFile26(
|
|
10858
|
-
|
|
11022
|
+
path39.join(absPath, ".gitignore"),
|
|
10859
11023
|
".ai/.cache/\n.ai/memories/personal/\n",
|
|
10860
11024
|
"utf8"
|
|
10861
11025
|
);
|
|
@@ -10870,7 +11034,7 @@ haive hub pull # import into a project
|
|
|
10870
11034
|
`
|
|
10871
11035
|
Next steps:
|
|
10872
11036
|
1. Add hubPath to your project's .ai/haive.config.json:
|
|
10873
|
-
{ "hubPath": "${
|
|
11037
|
+
{ "hubPath": "${path39.relative(process.cwd(), absPath)}" }
|
|
10874
11038
|
2. Run \`haive hub push\` to publish your shared memories
|
|
10875
11039
|
3. Share ${absPath} with teammates (git remote, NFS, etc.)
|
|
10876
11040
|
`
|
|
@@ -10889,9 +11053,9 @@ Next steps:
|
|
|
10889
11053
|
haive hub push --commit --message "feat: add payment API contract memories"
|
|
10890
11054
|
`
|
|
10891
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) => {
|
|
10892
|
-
const root =
|
|
10893
|
-
const paths =
|
|
10894
|
-
const config = await
|
|
11056
|
+
const root = findProjectRoot36(opts.dir);
|
|
11057
|
+
const paths = resolveHaivePaths33(root);
|
|
11058
|
+
const config = await loadConfig8(paths);
|
|
10895
11059
|
if (!config.hubPath) {
|
|
10896
11060
|
ui.error(
|
|
10897
11061
|
'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
|
|
@@ -10899,14 +11063,14 @@ Next steps:
|
|
|
10899
11063
|
process.exitCode = 1;
|
|
10900
11064
|
return;
|
|
10901
11065
|
}
|
|
10902
|
-
const hubRoot =
|
|
10903
|
-
if (!
|
|
11066
|
+
const hubRoot = path39.resolve(root, config.hubPath);
|
|
11067
|
+
if (!existsSync56(hubRoot)) {
|
|
10904
11068
|
ui.error(`Hub not found at ${hubRoot}. Run \`haive hub init ${config.hubPath}\` first.`);
|
|
10905
11069
|
process.exitCode = 1;
|
|
10906
11070
|
return;
|
|
10907
11071
|
}
|
|
10908
|
-
const projectName =
|
|
10909
|
-
const destDir =
|
|
11072
|
+
const projectName = path39.basename(root);
|
|
11073
|
+
const destDir = path39.join(hubRoot, ".ai", "memories", "shared", projectName);
|
|
10910
11074
|
await mkdir16(destDir, { recursive: true });
|
|
10911
11075
|
const all = await loadMemoriesFromDir28(paths.memoriesDir);
|
|
10912
11076
|
const shared = all.filter(
|
|
@@ -10925,7 +11089,7 @@ Next steps:
|
|
|
10925
11089
|
for (const { memory: memory2 } of shared) {
|
|
10926
11090
|
const fm = memory2.frontmatter;
|
|
10927
11091
|
const fileName = `${fm.id}.md`;
|
|
10928
|
-
const destPath =
|
|
11092
|
+
const destPath = path39.join(destDir, fileName);
|
|
10929
11093
|
await writeFile26(destPath, serializeMemory23(memory2), "utf8");
|
|
10930
11094
|
pushed++;
|
|
10931
11095
|
}
|
|
@@ -10933,7 +11097,7 @@ Next steps:
|
|
|
10933
11097
|
console.log(ui.dim(` Location: ${destDir}`));
|
|
10934
11098
|
if (opts.commit) {
|
|
10935
11099
|
const message = opts.message ?? `haive: sync shared memories from ${projectName} (${pushed} memories)`;
|
|
10936
|
-
spawnSync5("git", ["add",
|
|
11100
|
+
spawnSync5("git", ["add", path39.join(".ai", "memories", "shared", projectName)], {
|
|
10937
11101
|
cwd: hubRoot
|
|
10938
11102
|
});
|
|
10939
11103
|
const commit = spawnSync5("git", ["commit", "-m", message], {
|
|
@@ -10958,9 +11122,9 @@ Next steps:
|
|
|
10958
11122
|
hub.command("pull").description(
|
|
10959
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"
|
|
10960
11124
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
10961
|
-
const root =
|
|
10962
|
-
const paths =
|
|
10963
|
-
const config = await
|
|
11125
|
+
const root = findProjectRoot36(opts.dir);
|
|
11126
|
+
const paths = resolveHaivePaths33(root);
|
|
11127
|
+
const config = await loadConfig8(paths);
|
|
10964
11128
|
if (!config.hubPath) {
|
|
10965
11129
|
ui.error(
|
|
10966
11130
|
'hubPath not configured in .ai/haive.config.json.\n Add: { "hubPath": "../team-hub" }\n Or run: haive hub init <path> first.'
|
|
@@ -10968,13 +11132,13 @@ Next steps:
|
|
|
10968
11132
|
process.exitCode = 1;
|
|
10969
11133
|
return;
|
|
10970
11134
|
}
|
|
10971
|
-
const hubRoot =
|
|
10972
|
-
const hubSharedDir =
|
|
10973
|
-
if (!
|
|
11135
|
+
const hubRoot = path39.resolve(root, config.hubPath);
|
|
11136
|
+
const hubSharedDir = path39.join(hubRoot, ".ai", "memories", "shared");
|
|
11137
|
+
if (!existsSync56(hubSharedDir)) {
|
|
10974
11138
|
ui.warn("Hub has no shared memories yet. Run `haive hub push` from other projects first.");
|
|
10975
11139
|
return;
|
|
10976
11140
|
}
|
|
10977
|
-
const projectName =
|
|
11141
|
+
const projectName = path39.basename(root);
|
|
10978
11142
|
const { readdir: readdir7 } = await import("fs/promises");
|
|
10979
11143
|
const projectDirs = (await readdir7(hubSharedDir, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== projectName).map((d) => d.name);
|
|
10980
11144
|
if (projectDirs.length === 0) {
|
|
@@ -10984,17 +11148,17 @@ Next steps:
|
|
|
10984
11148
|
let totalImported = 0;
|
|
10985
11149
|
let totalUpdated = 0;
|
|
10986
11150
|
for (const sourceName of projectDirs) {
|
|
10987
|
-
const sourceDir =
|
|
10988
|
-
const destDir =
|
|
11151
|
+
const sourceDir = path39.join(hubSharedDir, sourceName);
|
|
11152
|
+
const destDir = path39.join(paths.memoriesDir, "shared", sourceName);
|
|
10989
11153
|
await mkdir16(destDir, { recursive: true });
|
|
10990
11154
|
const sourceFiles = (await readdir7(sourceDir)).filter((f) => f.endsWith(".md"));
|
|
10991
11155
|
const { loadMemoriesFromDir: loadDir } = await import("@hiveai/core");
|
|
10992
11156
|
const existingInDest = await loadDir(destDir);
|
|
10993
11157
|
const existingIds = new Set(existingInDest.map(({ memory: memory2 }) => memory2.frontmatter.id));
|
|
10994
11158
|
for (const file of sourceFiles) {
|
|
10995
|
-
const srcPath =
|
|
10996
|
-
const destPath =
|
|
10997
|
-
const fileContent = await
|
|
11159
|
+
const srcPath = path39.join(sourceDir, file);
|
|
11160
|
+
const destPath = path39.join(destDir, file);
|
|
11161
|
+
const fileContent = await readFile18(srcPath, "utf8");
|
|
10998
11162
|
const alreadyTagged = fileContent.includes(`cross-repo:${sourceName}`);
|
|
10999
11163
|
if (!alreadyTagged) {
|
|
11000
11164
|
await copyFile(srcPath, destPath);
|
|
@@ -11017,21 +11181,21 @@ Next steps:
|
|
|
11017
11181
|
);
|
|
11018
11182
|
});
|
|
11019
11183
|
hub.command("status").description("Show hub sync status.").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11020
|
-
const root =
|
|
11021
|
-
const paths =
|
|
11022
|
-
const config = await
|
|
11184
|
+
const root = findProjectRoot36(opts.dir);
|
|
11185
|
+
const paths = resolveHaivePaths33(root);
|
|
11186
|
+
const config = await loadConfig8(paths);
|
|
11023
11187
|
console.log(ui.bold("Hub status"));
|
|
11024
11188
|
console.log(
|
|
11025
11189
|
` hubPath: ${config.hubPath ? ui.green(config.hubPath) : ui.dim("not configured")}`
|
|
11026
11190
|
);
|
|
11027
|
-
const sharedDir =
|
|
11028
|
-
if (
|
|
11191
|
+
const sharedDir = path39.join(paths.memoriesDir, "shared");
|
|
11192
|
+
if (existsSync56(sharedDir)) {
|
|
11029
11193
|
const { readdir: readdir7 } = await import("fs/promises");
|
|
11030
11194
|
const sources = (await readdir7(sharedDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
11031
11195
|
console.log(`
|
|
11032
11196
|
Imported from ${sources.length} source(s):`);
|
|
11033
11197
|
for (const src of sources) {
|
|
11034
|
-
const files = (await readdir7(
|
|
11198
|
+
const files = (await readdir7(path39.join(sharedDir, src))).filter((f) => f.endsWith(".md"));
|
|
11035
11199
|
console.log(` ${src}: ${files.length} memor${files.length === 1 ? "y" : "ies"}`);
|
|
11036
11200
|
}
|
|
11037
11201
|
} else {
|
|
@@ -11046,7 +11210,7 @@ Next steps:
|
|
|
11046
11210
|
if (outgoing.length > 0) {
|
|
11047
11211
|
console.log(ui.dim(" Run `haive hub push` to publish them to the hub."));
|
|
11048
11212
|
}
|
|
11049
|
-
void
|
|
11213
|
+
void readFile18;
|
|
11050
11214
|
void writeFile26;
|
|
11051
11215
|
void saveConfig3;
|
|
11052
11216
|
});
|
|
@@ -11054,17 +11218,17 @@ Next steps:
|
|
|
11054
11218
|
|
|
11055
11219
|
// src/commands/stats.ts
|
|
11056
11220
|
import "commander";
|
|
11057
|
-
import { existsSync as
|
|
11221
|
+
import { existsSync as existsSync57 } from "fs";
|
|
11058
11222
|
import { mkdir as mkdir17, writeFile as writeFile27 } from "fs/promises";
|
|
11059
|
-
import
|
|
11223
|
+
import path40 from "path";
|
|
11060
11224
|
import {
|
|
11061
11225
|
aggregateUsage,
|
|
11062
|
-
findProjectRoot as
|
|
11226
|
+
findProjectRoot as findProjectRoot37,
|
|
11063
11227
|
loadMemoriesFromDir as loadMemoriesFromDir29,
|
|
11064
11228
|
loadUsageIndex as loadUsageIndex23,
|
|
11065
11229
|
parseSince,
|
|
11066
11230
|
readUsageEvents as readUsageEvents2,
|
|
11067
|
-
resolveHaivePaths as
|
|
11231
|
+
resolveHaivePaths as resolveHaivePaths34,
|
|
11068
11232
|
usageLogSize
|
|
11069
11233
|
} from "@hiveai/core";
|
|
11070
11234
|
function registerStats(program2) {
|
|
@@ -11073,8 +11237,8 @@ function registerStats(program2) {
|
|
|
11073
11237
|
"write a JSON rollup (tools + briefing counts + heuristic ROI hints). Parent dirs are created if needed.",
|
|
11074
11238
|
void 0
|
|
11075
11239
|
).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
11076
|
-
const root =
|
|
11077
|
-
const paths =
|
|
11240
|
+
const root = findProjectRoot37(opts.dir);
|
|
11241
|
+
const paths = resolveHaivePaths34(root);
|
|
11078
11242
|
if (opts.exportReport) {
|
|
11079
11243
|
await writeRoiReport(paths, root, opts.since ?? "30d", opts.exportReport);
|
|
11080
11244
|
return;
|
|
@@ -11128,11 +11292,11 @@ function registerStats(program2) {
|
|
|
11128
11292
|
});
|
|
11129
11293
|
}
|
|
11130
11294
|
async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
11131
|
-
const outAbs =
|
|
11295
|
+
const outAbs = path40.isAbsolute(outRelative) ? path40.resolve(outRelative) : path40.resolve(root, outRelative);
|
|
11132
11296
|
const size = await usageLogSize(paths);
|
|
11133
11297
|
let events = await readUsageEvents2(paths);
|
|
11134
11298
|
let memoryCount = { team: 0, personal: 0, total_skipped_session: 0 };
|
|
11135
|
-
if (
|
|
11299
|
+
if (existsSync57(paths.memoriesDir)) {
|
|
11136
11300
|
const mems = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
11137
11301
|
for (const { memory: memory2 } of mems) {
|
|
11138
11302
|
const fm = memory2.frontmatter;
|
|
@@ -11163,7 +11327,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
11163
11327
|
ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
|
|
11164
11328
|
events = [];
|
|
11165
11329
|
}
|
|
11166
|
-
await mkdir17(
|
|
11330
|
+
await mkdir17(path40.dirname(outAbs), { recursive: true });
|
|
11167
11331
|
const payload = {
|
|
11168
11332
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11169
11333
|
project_root: root,
|
|
@@ -11227,13 +11391,13 @@ import { performance } from "perf_hooks";
|
|
|
11227
11391
|
import "commander";
|
|
11228
11392
|
import {
|
|
11229
11393
|
estimateTokens as estimateTokens3,
|
|
11230
|
-
findProjectRoot as
|
|
11231
|
-
resolveHaivePaths as
|
|
11394
|
+
findProjectRoot as findProjectRoot38,
|
|
11395
|
+
resolveHaivePaths as resolveHaivePaths35
|
|
11232
11396
|
} from "@hiveai/core";
|
|
11233
11397
|
function registerBench(program2) {
|
|
11234
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) => {
|
|
11235
|
-
const root =
|
|
11236
|
-
const paths =
|
|
11399
|
+
const root = findProjectRoot38(opts.dir);
|
|
11400
|
+
const paths = resolveHaivePaths35(root);
|
|
11237
11401
|
const ctx = { paths };
|
|
11238
11402
|
const task = opts.task ?? "audit dependencies for security risks";
|
|
11239
11403
|
const scenarios = [
|
|
@@ -11352,11 +11516,11 @@ function summarize(name, t0, payload, notes) {
|
|
|
11352
11516
|
}
|
|
11353
11517
|
|
|
11354
11518
|
// src/commands/benchmark.ts
|
|
11355
|
-
import { existsSync as
|
|
11356
|
-
import { readdir as readdir5, readFile as
|
|
11357
|
-
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";
|
|
11358
11522
|
import "commander";
|
|
11359
|
-
import { estimateTokens as estimateTokens4, findProjectRoot as
|
|
11523
|
+
import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot39 } from "@hiveai/core";
|
|
11360
11524
|
function registerBenchmark(program2) {
|
|
11361
11525
|
const benchmark = program2.command("benchmark").description("Official hAIve benchmark/demo utilities for measuring agent enforcement value.");
|
|
11362
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) => {
|
|
@@ -11369,9 +11533,9 @@ function registerBenchmark(program2) {
|
|
|
11369
11533
|
}
|
|
11370
11534
|
const markdown = renderMarkdown(root, summary, rows);
|
|
11371
11535
|
if (opts.out) {
|
|
11372
|
-
const outFile =
|
|
11536
|
+
const outFile = path41.isAbsolute(opts.out) ? opts.out : path41.join(root, opts.out);
|
|
11373
11537
|
await writeFile28(outFile, markdown, "utf8");
|
|
11374
|
-
ui.success(`wrote ${
|
|
11538
|
+
ui.success(`wrote ${path41.relative(process.cwd(), outFile)}`);
|
|
11375
11539
|
return;
|
|
11376
11540
|
}
|
|
11377
11541
|
console.log(markdown);
|
|
@@ -11395,20 +11559,20 @@ function registerBenchmark(program2) {
|
|
|
11395
11559
|
}
|
|
11396
11560
|
function resolveBenchmarkRoot(dir) {
|
|
11397
11561
|
const candidate = dir ?? "benchmarks/agent-benchmark";
|
|
11398
|
-
if (
|
|
11399
|
-
const projectRoot =
|
|
11400
|
-
return
|
|
11562
|
+
if (path41.isAbsolute(candidate)) return candidate;
|
|
11563
|
+
const projectRoot = findProjectRoot39(process.cwd());
|
|
11564
|
+
return path41.join(projectRoot, candidate);
|
|
11401
11565
|
}
|
|
11402
11566
|
async function collectRows(root) {
|
|
11403
|
-
if (!
|
|
11567
|
+
if (!existsSync58(root)) throw new Error(`Benchmark directory not found: ${root}`);
|
|
11404
11568
|
const entries = await readdir5(root, { withFileTypes: true });
|
|
11405
11569
|
const rows = [];
|
|
11406
11570
|
for (const entry of entries) {
|
|
11407
11571
|
if (!entry.isDirectory()) continue;
|
|
11408
|
-
const fixtureDir =
|
|
11409
|
-
const reportFile =
|
|
11410
|
-
if (!
|
|
11411
|
-
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");
|
|
11412
11576
|
rows.push(parseAgentReport(entry.name, report));
|
|
11413
11577
|
}
|
|
11414
11578
|
return rows.sort((a, b) => a.fixture.localeCompare(b.fixture));
|
|
@@ -11498,19 +11662,19 @@ function escapeRegExp(value) {
|
|
|
11498
11662
|
|
|
11499
11663
|
// src/commands/memory-suggest.ts
|
|
11500
11664
|
import { mkdir as mkdir18, writeFile as writeFile29 } from "fs/promises";
|
|
11501
|
-
import { existsSync as
|
|
11502
|
-
import
|
|
11665
|
+
import { existsSync as existsSync59 } from "fs";
|
|
11666
|
+
import path43 from "path";
|
|
11503
11667
|
import "commander";
|
|
11504
11668
|
import {
|
|
11505
11669
|
aggregateUsage as aggregateUsage2,
|
|
11506
11670
|
buildFrontmatter as buildFrontmatter11,
|
|
11507
|
-
findProjectRoot as
|
|
11508
|
-
loadConfig as
|
|
11671
|
+
findProjectRoot as findProjectRoot40,
|
|
11672
|
+
loadConfig as loadConfig9,
|
|
11509
11673
|
loadMemoriesFromDir as loadMemoriesFromDir30,
|
|
11510
11674
|
memoryFilePath as memoryFilePath10,
|
|
11511
11675
|
parseSince as parseSince2,
|
|
11512
11676
|
readUsageEvents as readUsageEvents3,
|
|
11513
|
-
resolveHaivePaths as
|
|
11677
|
+
resolveHaivePaths as resolveHaivePaths36,
|
|
11514
11678
|
serializeMemory as serializeMemory24
|
|
11515
11679
|
} from "@hiveai/core";
|
|
11516
11680
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -11527,8 +11691,8 @@ function registerMemorySuggest(memory2) {
|
|
|
11527
11691
|
memory2.command("suggest").description(
|
|
11528
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."
|
|
11529
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) => {
|
|
11530
|
-
const root =
|
|
11531
|
-
const paths =
|
|
11694
|
+
const root = findProjectRoot40(opts.dir);
|
|
11695
|
+
const paths = resolveHaivePaths36(root);
|
|
11532
11696
|
const events = await readUsageEvents3(paths);
|
|
11533
11697
|
if (events.length === 0) {
|
|
11534
11698
|
if (opts.json) {
|
|
@@ -11566,7 +11730,7 @@ function registerMemorySuggest(memory2) {
|
|
|
11566
11730
|
inferred_type: inferType(v.tools, query)
|
|
11567
11731
|
})).sort((a, b) => b.count - a.count);
|
|
11568
11732
|
if (opts.autoSave) {
|
|
11569
|
-
const config = await
|
|
11733
|
+
const config = await loadConfig9(paths);
|
|
11570
11734
|
const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
|
|
11571
11735
|
const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
|
|
11572
11736
|
const status = config.defaultStatus === "validated" ? "validated" : "draft";
|
|
@@ -11577,7 +11741,7 @@ function registerMemorySuggest(memory2) {
|
|
|
11577
11741
|
}
|
|
11578
11742
|
const created = [];
|
|
11579
11743
|
const skipped = [];
|
|
11580
|
-
const existing =
|
|
11744
|
+
const existing = existsSync59(paths.memoriesDir) ? await loadMemoriesFromDir30(paths.memoriesDir) : [];
|
|
11581
11745
|
for (const s of top) {
|
|
11582
11746
|
const slug = slugify2(s.query);
|
|
11583
11747
|
if (!slug) {
|
|
@@ -11600,13 +11764,13 @@ function registerMemorySuggest(memory2) {
|
|
|
11600
11764
|
});
|
|
11601
11765
|
const body = renderTemplate(s, fm.id, status);
|
|
11602
11766
|
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
11603
|
-
await mkdir18(
|
|
11604
|
-
if (
|
|
11605
|
-
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)}` });
|
|
11606
11770
|
continue;
|
|
11607
11771
|
}
|
|
11608
11772
|
await writeFile29(file, serializeMemory24({ frontmatter: fm, body }), "utf8");
|
|
11609
|
-
created.push({ id: fm.id, file:
|
|
11773
|
+
created.push({ id: fm.id, file: path43.relative(root, file), query: s.query });
|
|
11610
11774
|
}
|
|
11611
11775
|
if (opts.json) {
|
|
11612
11776
|
console.log(JSON.stringify({ created, skipped }, null, 2));
|
|
@@ -11704,16 +11868,16 @@ function truncate2(text, max) {
|
|
|
11704
11868
|
}
|
|
11705
11869
|
|
|
11706
11870
|
// src/commands/memory-archive.ts
|
|
11707
|
-
import { existsSync as
|
|
11871
|
+
import { existsSync as existsSync60 } from "fs";
|
|
11708
11872
|
import { writeFile as writeFile30 } from "fs/promises";
|
|
11709
|
-
import
|
|
11873
|
+
import path44 from "path";
|
|
11710
11874
|
import "commander";
|
|
11711
11875
|
import {
|
|
11712
|
-
findProjectRoot as
|
|
11876
|
+
findProjectRoot as findProjectRoot41,
|
|
11713
11877
|
getUsage as getUsage18,
|
|
11714
11878
|
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
11715
11879
|
loadUsageIndex as loadUsageIndex24,
|
|
11716
|
-
resolveHaivePaths as
|
|
11880
|
+
resolveHaivePaths as resolveHaivePaths37,
|
|
11717
11881
|
serializeMemory as serializeMemory25
|
|
11718
11882
|
} from "@hiveai/core";
|
|
11719
11883
|
var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
|
|
@@ -11721,9 +11885,9 @@ function registerMemoryArchive(memory2) {
|
|
|
11721
11885
|
memory2.command("archive").description(
|
|
11722
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."
|
|
11723
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) => {
|
|
11724
|
-
const root =
|
|
11725
|
-
const paths =
|
|
11726
|
-
if (!
|
|
11888
|
+
const root = findProjectRoot41(opts.dir);
|
|
11889
|
+
const paths = resolveHaivePaths37(root);
|
|
11890
|
+
if (!existsSync60(paths.memoriesDir)) {
|
|
11727
11891
|
ui.error(`No .ai/memories at ${root}. Run \`haive init\` first.`);
|
|
11728
11892
|
process.exitCode = 1;
|
|
11729
11893
|
return;
|
|
@@ -11744,7 +11908,7 @@ function registerMemoryArchive(memory2) {
|
|
|
11744
11908
|
if (typeFilter && fm.type !== typeFilter) continue;
|
|
11745
11909
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
11746
11910
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
11747
|
-
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)));
|
|
11748
11912
|
const isAnchorless = !hasAnyAnchor;
|
|
11749
11913
|
if (!isAnchorless && !allPathsGone) continue;
|
|
11750
11914
|
const u = getUsage18(usage, fm.id);
|
|
@@ -11818,33 +11982,33 @@ function parseDays(input) {
|
|
|
11818
11982
|
}
|
|
11819
11983
|
|
|
11820
11984
|
// src/commands/doctor.ts
|
|
11821
|
-
import { existsSync as
|
|
11822
|
-
import { readFile as
|
|
11823
|
-
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";
|
|
11824
11988
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
11825
11989
|
import "commander";
|
|
11826
11990
|
import {
|
|
11827
11991
|
codeMapPath as codeMapPath2,
|
|
11828
|
-
findProjectRoot as
|
|
11992
|
+
findProjectRoot as findProjectRoot42,
|
|
11829
11993
|
getUsage as getUsage19,
|
|
11830
11994
|
loadCodeMap as loadCodeMap6,
|
|
11831
|
-
loadConfig as
|
|
11995
|
+
loadConfig as loadConfig10,
|
|
11832
11996
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
11833
11997
|
loadUsageIndex as loadUsageIndex25,
|
|
11834
11998
|
readUsageEvents as readUsageEvents4,
|
|
11835
|
-
resolveHaivePaths as
|
|
11999
|
+
resolveHaivePaths as resolveHaivePaths38
|
|
11836
12000
|
} from "@hiveai/core";
|
|
11837
12001
|
var MS_PER_DAY3 = 24 * 60 * 60 * 1e3;
|
|
11838
12002
|
function registerDoctor(program2) {
|
|
11839
12003
|
program2.command("doctor").description(
|
|
11840
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."
|
|
11841
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) => {
|
|
11842
|
-
const root =
|
|
11843
|
-
const paths =
|
|
12006
|
+
const root = findProjectRoot42(opts.dir);
|
|
12007
|
+
const paths = resolveHaivePaths38(root);
|
|
11844
12008
|
const findings = [];
|
|
11845
12009
|
const repairs = [];
|
|
11846
|
-
const config = await
|
|
11847
|
-
if (!
|
|
12010
|
+
const config = await loadConfig10(paths);
|
|
12011
|
+
if (!existsSync61(paths.haiveDir)) {
|
|
11848
12012
|
findings.push({
|
|
11849
12013
|
severity: "error",
|
|
11850
12014
|
code: "not-initialized",
|
|
@@ -11865,7 +12029,7 @@ function registerDoctor(program2) {
|
|
|
11865
12029
|
})
|
|
11866
12030
|
);
|
|
11867
12031
|
}
|
|
11868
|
-
if (!
|
|
12032
|
+
if (!existsSync61(paths.projectContext)) {
|
|
11869
12033
|
findings.push({
|
|
11870
12034
|
severity: "warn",
|
|
11871
12035
|
code: "no-project-context",
|
|
@@ -11873,8 +12037,8 @@ function registerDoctor(program2) {
|
|
|
11873
12037
|
fix: "haive init"
|
|
11874
12038
|
});
|
|
11875
12039
|
} else {
|
|
11876
|
-
const { readFile:
|
|
11877
|
-
const content = await
|
|
12040
|
+
const { readFile: readFile23 } = await import("fs/promises");
|
|
12041
|
+
const content = await readFile23(paths.projectContext, "utf8");
|
|
11878
12042
|
const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `haive init`");
|
|
11879
12043
|
if (isTemplate) {
|
|
11880
12044
|
findings.push({
|
|
@@ -11894,7 +12058,7 @@ function registerDoctor(program2) {
|
|
|
11894
12058
|
});
|
|
11895
12059
|
}
|
|
11896
12060
|
}
|
|
11897
|
-
const memories =
|
|
12061
|
+
const memories = existsSync61(paths.memoriesDir) ? await loadMemoriesFromDir33(paths.memoriesDir) : [];
|
|
11898
12062
|
const now = Date.now();
|
|
11899
12063
|
if (memories.length === 0) {
|
|
11900
12064
|
findings.push({
|
|
@@ -12035,12 +12199,12 @@ function registerDoctor(program2) {
|
|
|
12035
12199
|
}
|
|
12036
12200
|
}
|
|
12037
12201
|
if (config.enforcement?.requireBriefingFirst) {
|
|
12038
|
-
const claudeSettings =
|
|
12202
|
+
const claudeSettings = path45.join(root, ".claude", "settings.local.json");
|
|
12039
12203
|
let hasClaudeEnforcement = false;
|
|
12040
|
-
if (
|
|
12204
|
+
if (existsSync61(claudeSettings)) {
|
|
12041
12205
|
try {
|
|
12042
|
-
const { readFile:
|
|
12043
|
-
const raw = await
|
|
12206
|
+
const { readFile: readFile23 } = await import("fs/promises");
|
|
12207
|
+
const raw = await readFile23(claudeSettings, "utf8");
|
|
12044
12208
|
hasClaudeEnforcement = raw.includes("haive enforce session-start") && raw.includes("haive enforce pre-tool-use");
|
|
12045
12209
|
} catch {
|
|
12046
12210
|
hasClaudeEnforcement = false;
|
|
@@ -12063,14 +12227,14 @@ function registerDoctor(program2) {
|
|
|
12063
12227
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12064
12228
|
});
|
|
12065
12229
|
}
|
|
12066
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
12230
|
+
findings.push(...await collectInstallFindings(root, "0.9.29"));
|
|
12067
12231
|
try {
|
|
12068
12232
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12069
12233
|
encoding: "utf8",
|
|
12070
12234
|
timeout: 3e3,
|
|
12071
12235
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12072
12236
|
}).trim();
|
|
12073
|
-
const cliVersion = "0.9.
|
|
12237
|
+
const cliVersion = "0.9.29";
|
|
12074
12238
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12075
12239
|
findings.push({
|
|
12076
12240
|
severity: "warn",
|
|
@@ -12086,17 +12250,17 @@ npm uninstall -g @hiveai/mcp`
|
|
|
12086
12250
|
}
|
|
12087
12251
|
{
|
|
12088
12252
|
const configPaths = [
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12253
|
+
path45.join(root, ".mcp.json"),
|
|
12254
|
+
path45.join(root, ".cursor", "mcp.json"),
|
|
12255
|
+
path45.join(root, ".vscode", "mcp.json")
|
|
12092
12256
|
];
|
|
12093
12257
|
const staleConfigs = [];
|
|
12094
12258
|
for (const cfgPath of configPaths) {
|
|
12095
|
-
if (!
|
|
12259
|
+
if (!existsSync61(cfgPath)) continue;
|
|
12096
12260
|
try {
|
|
12097
|
-
const raw = await
|
|
12261
|
+
const raw = await readFile20(cfgPath, "utf8");
|
|
12098
12262
|
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
12099
|
-
staleConfigs.push(
|
|
12263
|
+
staleConfigs.push(path45.relative(root, cfgPath));
|
|
12100
12264
|
if (opts.fix && !opts.dryRun) {
|
|
12101
12265
|
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
12102
12266
|
await writeFile31(cfgPath, updated, "utf8");
|
|
@@ -12386,9 +12550,9 @@ which -a haive`
|
|
|
12386
12550
|
".vscode/mcp.json"
|
|
12387
12551
|
];
|
|
12388
12552
|
for (const rel of integrationFiles) {
|
|
12389
|
-
const file =
|
|
12390
|
-
if (!
|
|
12391
|
-
const text = await
|
|
12553
|
+
const file = path45.join(root, rel);
|
|
12554
|
+
if (!existsSync61(file)) continue;
|
|
12555
|
+
const text = await readFile20(file, "utf8").catch(() => "");
|
|
12392
12556
|
for (const bin of extractAbsoluteHaiveBins(text)) {
|
|
12393
12557
|
const version = versionForBinary(bin);
|
|
12394
12558
|
if (!version) {
|
|
@@ -12412,7 +12576,7 @@ which -a haive`
|
|
|
12412
12576
|
}
|
|
12413
12577
|
async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
12414
12578
|
const findings = [];
|
|
12415
|
-
const rootPkg = await readJson(
|
|
12579
|
+
const rootPkg = await readJson(path45.join(root, "package.json"));
|
|
12416
12580
|
const workspacePackages = [
|
|
12417
12581
|
"packages/core/package.json",
|
|
12418
12582
|
"packages/embeddings/package.json",
|
|
@@ -12421,7 +12585,7 @@ async function collectWorkspaceVersionFindings(root, expectedVersion) {
|
|
|
12421
12585
|
];
|
|
12422
12586
|
const existing = (await Promise.all(workspacePackages.map(async (rel) => ({
|
|
12423
12587
|
rel,
|
|
12424
|
-
pkg: await readJson(
|
|
12588
|
+
pkg: await readJson(path45.join(root, rel))
|
|
12425
12589
|
})))).filter((item) => item.pkg);
|
|
12426
12590
|
const isHaiveWorkspace = rootPkg?.name === "haive-monorepo" || existing.some((item) => item.pkg?.name?.startsWith("@hiveai/"));
|
|
12427
12591
|
if (!isHaiveWorkspace) return findings;
|
|
@@ -12483,9 +12647,9 @@ function collectGlobalHivemoduleFindings(expectedVersion) {
|
|
|
12483
12647
|
}
|
|
12484
12648
|
}
|
|
12485
12649
|
async function readJson(file) {
|
|
12486
|
-
if (!
|
|
12650
|
+
if (!existsSync61(file)) return null;
|
|
12487
12651
|
try {
|
|
12488
|
-
return JSON.parse(await
|
|
12652
|
+
return JSON.parse(await readFile20(file, "utf8"));
|
|
12489
12653
|
} catch {
|
|
12490
12654
|
return null;
|
|
12491
12655
|
}
|
|
@@ -12530,22 +12694,22 @@ function extractAbsoluteHaiveBins(text) {
|
|
|
12530
12694
|
}
|
|
12531
12695
|
|
|
12532
12696
|
// src/commands/playback.ts
|
|
12533
|
-
import { existsSync as
|
|
12697
|
+
import { existsSync as existsSync63 } from "fs";
|
|
12534
12698
|
import "commander";
|
|
12535
12699
|
import {
|
|
12536
|
-
findProjectRoot as
|
|
12700
|
+
findProjectRoot as findProjectRoot43,
|
|
12537
12701
|
loadMemoriesFromDir as loadMemoriesFromDir34,
|
|
12538
12702
|
parseSince as parseSince3,
|
|
12539
12703
|
readUsageEvents as readUsageEvents5,
|
|
12540
|
-
resolveHaivePaths as
|
|
12704
|
+
resolveHaivePaths as resolveHaivePaths39
|
|
12541
12705
|
} from "@hiveai/core";
|
|
12542
12706
|
var MS_PER_MINUTE = 6e4;
|
|
12543
12707
|
function registerPlayback(program2) {
|
|
12544
12708
|
program2.command("playback").description(
|
|
12545
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?'"
|
|
12546
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) => {
|
|
12547
|
-
const root =
|
|
12548
|
-
const paths =
|
|
12711
|
+
const root = findProjectRoot43(opts.dir);
|
|
12712
|
+
const paths = resolveHaivePaths39(root);
|
|
12549
12713
|
const events = await readUsageEvents5(paths);
|
|
12550
12714
|
if (events.length === 0) {
|
|
12551
12715
|
if (opts.json) {
|
|
@@ -12560,7 +12724,7 @@ function registerPlayback(program2) {
|
|
|
12560
12724
|
const filtered = cutoff > 0 ? events.filter((e) => Date.parse(e.at) >= cutoff) : events;
|
|
12561
12725
|
const gapMs = Math.max(1, parseInt(opts.sessionGap ?? "30", 10)) * MS_PER_MINUTE;
|
|
12562
12726
|
const sessions = bucketSessions(filtered, gapMs);
|
|
12563
|
-
const all =
|
|
12727
|
+
const all = existsSync63(paths.memoriesDir) ? await loadMemoriesFromDir34(paths.memoriesDir) : [];
|
|
12564
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);
|
|
12565
12729
|
const enriched = sessions.map((s, i) => {
|
|
12566
12730
|
const startMs = Date.parse(s.start);
|
|
@@ -12650,8 +12814,8 @@ function truncate3(text, max) {
|
|
|
12650
12814
|
import { spawn as spawn5 } from "child_process";
|
|
12651
12815
|
import "commander";
|
|
12652
12816
|
import {
|
|
12653
|
-
findProjectRoot as
|
|
12654
|
-
resolveHaivePaths as
|
|
12817
|
+
findProjectRoot as findProjectRoot44,
|
|
12818
|
+
resolveHaivePaths as resolveHaivePaths40
|
|
12655
12819
|
} from "@hiveai/core";
|
|
12656
12820
|
function registerPrecommit(program2) {
|
|
12657
12821
|
program2.command("precommit").description(
|
|
@@ -12661,8 +12825,8 @@ function registerPrecommit(program2) {
|
|
|
12661
12825
|
"'any' | 'high-confidence' (default) | 'never' (report only)",
|
|
12662
12826
|
"high-confidence"
|
|
12663
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) => {
|
|
12664
|
-
const root =
|
|
12665
|
-
const paths =
|
|
12828
|
+
const root = findProjectRoot44(opts.dir);
|
|
12829
|
+
const paths = resolveHaivePaths40(root);
|
|
12666
12830
|
const ctx = { paths };
|
|
12667
12831
|
let diff = "";
|
|
12668
12832
|
let touchedPaths = opts.paths ?? [];
|
|
@@ -12792,12 +12956,12 @@ function runCommand3(cmd, args, cwd) {
|
|
|
12792
12956
|
}
|
|
12793
12957
|
|
|
12794
12958
|
// src/commands/welcome.ts
|
|
12795
|
-
import { existsSync as
|
|
12959
|
+
import { existsSync as existsSync64 } from "fs";
|
|
12796
12960
|
import "commander";
|
|
12797
12961
|
import {
|
|
12798
|
-
findProjectRoot as
|
|
12962
|
+
findProjectRoot as findProjectRoot45,
|
|
12799
12963
|
loadMemoriesFromDir as loadMemoriesFromDir35,
|
|
12800
|
-
resolveHaivePaths as
|
|
12964
|
+
resolveHaivePaths as resolveHaivePaths41
|
|
12801
12965
|
} from "@hiveai/core";
|
|
12802
12966
|
var TYPE_RANK = {
|
|
12803
12967
|
skill: 0,
|
|
@@ -12812,9 +12976,9 @@ function registerWelcome(program2) {
|
|
|
12812
12976
|
program2.command("welcome").description(
|
|
12813
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"
|
|
12814
12978
|
).option("--limit <n>", "maximum memories listed", "20").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12815
|
-
const root =
|
|
12816
|
-
const paths =
|
|
12817
|
-
if (!
|
|
12979
|
+
const root = findProjectRoot45(opts.dir);
|
|
12980
|
+
const paths = resolveHaivePaths41(root);
|
|
12981
|
+
if (!existsSync64(paths.memoriesDir)) {
|
|
12818
12982
|
ui.error(`No memories at ${paths.memoriesDir}. Run 'haive init' first.`);
|
|
12819
12983
|
process.exitCode = 1;
|
|
12820
12984
|
return;
|
|
@@ -12873,27 +13037,27 @@ function registerMemorySuggestTopic(memory2) {
|
|
|
12873
13037
|
}
|
|
12874
13038
|
|
|
12875
13039
|
// src/commands/resolve-project.ts
|
|
12876
|
-
import
|
|
13040
|
+
import path46 from "path";
|
|
12877
13041
|
import "commander";
|
|
12878
13042
|
import { resolveProjectInfo as resolveProjectInfo2 } from "@hiveai/core";
|
|
12879
13043
|
function registerResolveProject(program2) {
|
|
12880
13044
|
program2.command("resolve-project").description(
|
|
12881
13045
|
"Print JSON for hAIve project root resolution (HAIVE_PROJECT_ROOT, markers, .ai layout)."
|
|
12882
13046
|
).option("-d, --dir <dir>", "working directory", process.cwd()).action((opts) => {
|
|
12883
|
-
const info = resolveProjectInfo2({ cwd:
|
|
13047
|
+
const info = resolveProjectInfo2({ cwd: path46.resolve(opts.dir) });
|
|
12884
13048
|
console.log(JSON.stringify({ ok: true, info }, null, 2));
|
|
12885
13049
|
});
|
|
12886
13050
|
}
|
|
12887
13051
|
|
|
12888
13052
|
// src/commands/runtime-journal.ts
|
|
12889
|
-
import { existsSync as
|
|
12890
|
-
import
|
|
13053
|
+
import { existsSync as existsSync65 } from "fs";
|
|
13054
|
+
import path47 from "path";
|
|
12891
13055
|
import "commander";
|
|
12892
13056
|
import {
|
|
12893
13057
|
appendRuntimeJournalEntry as appendRuntimeJournalEntry3,
|
|
12894
|
-
findProjectRoot as
|
|
13058
|
+
findProjectRoot as findProjectRoot46,
|
|
12895
13059
|
readRuntimeJournalTail as readRuntimeJournalTail2,
|
|
12896
|
-
resolveHaivePaths as
|
|
13060
|
+
resolveHaivePaths as resolveHaivePaths42
|
|
12897
13061
|
} from "@hiveai/core";
|
|
12898
13062
|
function registerRuntime(program2) {
|
|
12899
13063
|
const runtime = program2.command("runtime").description(
|
|
@@ -12901,18 +13065,18 @@ function registerRuntime(program2) {
|
|
|
12901
13065
|
);
|
|
12902
13066
|
const journal = runtime.command("journal").description("Append or read the machine-local session journal (NDJSON)");
|
|
12903
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) => {
|
|
12904
|
-
const root =
|
|
12905
|
-
const paths =
|
|
13068
|
+
const root = path47.resolve(opts.dir ?? process.cwd());
|
|
13069
|
+
const paths = resolveHaivePaths42(findProjectRoot46(root));
|
|
12906
13070
|
const raw = opts.kind ?? "note";
|
|
12907
13071
|
const kind = ["note", "session_end", "mcp"].includes(raw) ? raw : "note";
|
|
12908
13072
|
await appendRuntimeJournalEntry3(paths, { kind, message });
|
|
12909
|
-
ui.success(`Appended to ${
|
|
13073
|
+
ui.success(`Appended to ${path47.relative(root, paths.runtimeDir)}/session-journal.ndjson`);
|
|
12910
13074
|
});
|
|
12911
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) => {
|
|
12912
|
-
const root =
|
|
12913
|
-
const paths =
|
|
13076
|
+
const root = path47.resolve(opts.dir ?? process.cwd());
|
|
13077
|
+
const paths = resolveHaivePaths42(findProjectRoot46(root));
|
|
12914
13078
|
const limit = Math.min(500, Math.max(1, parseInt(opts.limit, 10) || 30));
|
|
12915
|
-
if (!
|
|
13079
|
+
if (!existsSync65(paths.haiveDir)) {
|
|
12916
13080
|
ui.error("No .ai/ \u2014 run `haive init` first.");
|
|
12917
13081
|
process.exitCode = 1;
|
|
12918
13082
|
return;
|
|
@@ -12927,13 +13091,13 @@ function registerRuntime(program2) {
|
|
|
12927
13091
|
}
|
|
12928
13092
|
|
|
12929
13093
|
// src/commands/memory-timeline.ts
|
|
12930
|
-
import { existsSync as
|
|
12931
|
-
import
|
|
13094
|
+
import { existsSync as existsSync66 } from "fs";
|
|
13095
|
+
import path48 from "path";
|
|
12932
13096
|
import "commander";
|
|
12933
13097
|
import {
|
|
12934
13098
|
collectTimelineEntries as collectTimelineEntries2,
|
|
12935
|
-
findProjectRoot as
|
|
12936
|
-
resolveHaivePaths as
|
|
13099
|
+
findProjectRoot as findProjectRoot47,
|
|
13100
|
+
resolveHaivePaths as resolveHaivePaths43
|
|
12937
13101
|
} from "@hiveai/core";
|
|
12938
13102
|
function registerMemoryTimeline(memory2) {
|
|
12939
13103
|
memory2.command("timeline").description(
|
|
@@ -12944,9 +13108,9 @@ function registerMemoryTimeline(memory2) {
|
|
|
12944
13108
|
process.exitCode = 1;
|
|
12945
13109
|
return;
|
|
12946
13110
|
}
|
|
12947
|
-
const root =
|
|
12948
|
-
const paths =
|
|
12949
|
-
if (!
|
|
13111
|
+
const root = path48.resolve(opts.dir ?? process.cwd());
|
|
13112
|
+
const paths = resolveHaivePaths43(findProjectRoot47(root));
|
|
13113
|
+
if (!existsSync66(paths.memoriesDir)) {
|
|
12950
13114
|
ui.error("No memories \u2014 run `haive init`.");
|
|
12951
13115
|
process.exitCode = 1;
|
|
12952
13116
|
return;
|
|
@@ -12964,14 +13128,14 @@ function registerMemoryTimeline(memory2) {
|
|
|
12964
13128
|
}
|
|
12965
13129
|
|
|
12966
13130
|
// src/commands/memory-conflict-candidates.ts
|
|
12967
|
-
import { existsSync as
|
|
12968
|
-
import
|
|
13131
|
+
import { existsSync as existsSync67 } from "fs";
|
|
13132
|
+
import path49 from "path";
|
|
12969
13133
|
import "commander";
|
|
12970
13134
|
import {
|
|
12971
13135
|
findLexicalConflictPairs as findLexicalConflictPairs2,
|
|
12972
13136
|
findTopicStatusConflictPairs as findTopicStatusConflictPairs2,
|
|
12973
|
-
findProjectRoot as
|
|
12974
|
-
resolveHaivePaths as
|
|
13137
|
+
findProjectRoot as findProjectRoot48,
|
|
13138
|
+
resolveHaivePaths as resolveHaivePaths44
|
|
12975
13139
|
} from "@hiveai/core";
|
|
12976
13140
|
function parseTypes(csv) {
|
|
12977
13141
|
const allowed = ["decision", "architecture", "convention", "gotcha"];
|
|
@@ -12987,9 +13151,9 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
12987
13151
|
"decision,architecture,convention,gotcha (lexical scan)",
|
|
12988
13152
|
"decision,architecture"
|
|
12989
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) => {
|
|
12990
|
-
const root =
|
|
12991
|
-
const paths =
|
|
12992
|
-
if (!
|
|
13154
|
+
const root = path49.resolve(opts.dir ?? process.cwd());
|
|
13155
|
+
const paths = resolveHaivePaths44(findProjectRoot48(root));
|
|
13156
|
+
if (!existsSync67(paths.memoriesDir)) {
|
|
12993
13157
|
ui.error("No memories \u2014 run `haive init`.");
|
|
12994
13158
|
process.exitCode = 1;
|
|
12995
13159
|
return;
|
|
@@ -13025,24 +13189,24 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
13025
13189
|
|
|
13026
13190
|
// src/commands/enforce.ts
|
|
13027
13191
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
13028
|
-
import { existsSync as
|
|
13029
|
-
import { chmod as chmod2, mkdir as mkdir19, readFile as
|
|
13030
|
-
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";
|
|
13031
13195
|
import "commander";
|
|
13032
13196
|
import {
|
|
13033
|
-
findProjectRoot as
|
|
13197
|
+
findProjectRoot as findProjectRoot49,
|
|
13034
13198
|
hasRecentBriefingMarker,
|
|
13035
13199
|
isFreshIsoDate,
|
|
13036
|
-
loadConfig as
|
|
13200
|
+
loadConfig as loadConfig11,
|
|
13037
13201
|
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
13038
13202
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
13039
13203
|
readRecentBriefingMarker,
|
|
13040
13204
|
resolveBriefingBudget as resolveBriefingBudget3,
|
|
13041
|
-
resolveHaivePaths as
|
|
13205
|
+
resolveHaivePaths as resolveHaivePaths45,
|
|
13042
13206
|
saveConfig as saveConfig4,
|
|
13043
13207
|
SESSION_RECAP_TTL_MS,
|
|
13044
13208
|
verifyAnchor as verifyAnchor4,
|
|
13045
|
-
writeBriefingMarker as
|
|
13209
|
+
writeBriefingMarker as writeBriefingMarker3
|
|
13046
13210
|
} from "@hiveai/core";
|
|
13047
13211
|
var MAX_STDIN_BYTES2 = 256 * 1024;
|
|
13048
13212
|
var ENFORCE_HOOK_MARKER = "# hAIve enforcement hook";
|
|
@@ -13051,10 +13215,10 @@ function registerEnforce(program2) {
|
|
|
13051
13215
|
"Agent-agnostic enforcement helpers: install policy gates, report status, and block unsafe workflows."
|
|
13052
13216
|
);
|
|
13053
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) => {
|
|
13054
|
-
const root =
|
|
13055
|
-
const paths =
|
|
13218
|
+
const root = findProjectRoot49(opts.dir);
|
|
13219
|
+
const paths = resolveHaivePaths45(root);
|
|
13056
13220
|
await mkdir19(paths.haiveDir, { recursive: true });
|
|
13057
|
-
const current = await
|
|
13221
|
+
const current = await loadConfig11(paths);
|
|
13058
13222
|
await saveConfig4(paths, {
|
|
13059
13223
|
...current,
|
|
13060
13224
|
enforcement: {
|
|
@@ -13077,7 +13241,7 @@ function registerEnforce(program2) {
|
|
|
13077
13241
|
if (opts.claude !== false) {
|
|
13078
13242
|
try {
|
|
13079
13243
|
const result = await installClaudeHooksAtPath(defaultClaudeSettingsPath("project", root));
|
|
13080
|
-
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${
|
|
13244
|
+
ui.success(`${result.created ? "Created" : "Patched"} Claude Code hooks (${path50.relative(root, result.settingsPath)})`);
|
|
13081
13245
|
} catch (err) {
|
|
13082
13246
|
ui.warn(`Claude Code hooks not installed: ${err instanceof Error ? err.message : String(err)}`);
|
|
13083
13247
|
}
|
|
@@ -13096,21 +13260,21 @@ function registerEnforce(program2) {
|
|
|
13096
13260
|
if (report.should_block) process.exit(2);
|
|
13097
13261
|
});
|
|
13098
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) => {
|
|
13099
|
-
const root =
|
|
13100
|
-
const paths =
|
|
13101
|
-
const cacheDir =
|
|
13102
|
-
if (
|
|
13103
|
-
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)`);
|
|
13104
13268
|
else {
|
|
13105
13269
|
const removed = await cleanupCacheDir(cacheDir);
|
|
13106
|
-
ui.success(`cleaned ${
|
|
13270
|
+
ui.success(`cleaned ${path50.relative(root, cacheDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
13107
13271
|
}
|
|
13108
13272
|
}
|
|
13109
|
-
if (
|
|
13110
|
-
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)`);
|
|
13111
13275
|
else {
|
|
13112
13276
|
const removed = await cleanupRuntimeDir(paths.runtimeDir);
|
|
13113
|
-
ui.success(`cleaned ${
|
|
13277
|
+
ui.success(`cleaned ${path50.relative(root, paths.runtimeDir)}${removed > 0 ? ` (${removed} item${removed === 1 ? "" : "s"} removed)` : ""}`);
|
|
13114
13278
|
}
|
|
13115
13279
|
}
|
|
13116
13280
|
});
|
|
@@ -13123,8 +13287,8 @@ function registerEnforce(program2) {
|
|
|
13123
13287
|
const payload = await readHookPayload();
|
|
13124
13288
|
const root = resolveRoot(opts.dir, payload);
|
|
13125
13289
|
if (!root) return;
|
|
13126
|
-
const paths =
|
|
13127
|
-
if (!
|
|
13290
|
+
const paths = resolveHaivePaths45(root);
|
|
13291
|
+
if (!existsSync68(paths.haiveDir)) return;
|
|
13128
13292
|
await mkdir19(paths.runtimeDir, { recursive: true });
|
|
13129
13293
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
13130
13294
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this hAIve-initialized project.";
|
|
@@ -13152,7 +13316,7 @@ function registerEnforce(program2) {
|
|
|
13152
13316
|
},
|
|
13153
13317
|
{ paths }
|
|
13154
13318
|
);
|
|
13155
|
-
await
|
|
13319
|
+
await writeBriefingMarker3(paths, {
|
|
13156
13320
|
sessionId,
|
|
13157
13321
|
task,
|
|
13158
13322
|
source: opts.source ?? "claude-session-start",
|
|
@@ -13186,8 +13350,8 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
13186
13350
|
const payload = await readHookPayload();
|
|
13187
13351
|
const root = resolveRoot(opts.dir, payload);
|
|
13188
13352
|
if (!root) return;
|
|
13189
|
-
const paths =
|
|
13190
|
-
if (!
|
|
13353
|
+
const paths = resolveHaivePaths45(root);
|
|
13354
|
+
if (!existsSync68(paths.haiveDir)) return;
|
|
13191
13355
|
if (!isWriteLikeTool(payload)) return;
|
|
13192
13356
|
const ok = await hasRecentBriefingMarker(paths, payload.session_id);
|
|
13193
13357
|
if (ok) {
|
|
@@ -13229,15 +13393,15 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
13229
13393
|
});
|
|
13230
13394
|
}
|
|
13231
13395
|
async function runWithEnforcement(command, args, opts) {
|
|
13232
|
-
const root =
|
|
13233
|
-
const paths =
|
|
13234
|
-
if (!
|
|
13396
|
+
const root = findProjectRoot49(opts.dir);
|
|
13397
|
+
const paths = resolveHaivePaths45(root);
|
|
13398
|
+
if (!existsSync68(paths.haiveDir)) {
|
|
13235
13399
|
ui.error(`No .ai/ found at ${root}. Run \`haive init\` first.`);
|
|
13236
13400
|
process.exit(1);
|
|
13237
13401
|
}
|
|
13238
13402
|
const sessionId = `haive-run-${process.pid}-${Date.now()}`;
|
|
13239
13403
|
const task = opts.task ?? `Run agent command: ${[command, ...args].join(" ")}`;
|
|
13240
|
-
await
|
|
13404
|
+
await writeBriefingMarker3(paths, {
|
|
13241
13405
|
sessionId,
|
|
13242
13406
|
task,
|
|
13243
13407
|
source: "haive-run"
|
|
@@ -13250,7 +13414,7 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
13250
13414
|
process.exit(2);
|
|
13251
13415
|
}
|
|
13252
13416
|
ui.info(`hAIve briefing marker created for wrapped agent session: ${sessionId}`);
|
|
13253
|
-
ui.info(`Briefing written to ${
|
|
13417
|
+
ui.info(`Briefing written to ${path50.relative(root, briefingFile)} and exported as HAIVE_BRIEFING_FILE`);
|
|
13254
13418
|
const child = spawn6(command, args, {
|
|
13255
13419
|
cwd: root,
|
|
13256
13420
|
stdio: "inherit",
|
|
@@ -13294,15 +13458,15 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13294
13458
|
min_semantic_score: 0.25,
|
|
13295
13459
|
budget_preset: "quick"
|
|
13296
13460
|
}, { paths });
|
|
13297
|
-
await
|
|
13461
|
+
await writeBriefingMarker3(paths, {
|
|
13298
13462
|
sessionId,
|
|
13299
13463
|
task,
|
|
13300
13464
|
source: "haive-run",
|
|
13301
13465
|
memoryIds: briefing.memories.map((m) => m.id)
|
|
13302
13466
|
});
|
|
13303
|
-
const dir =
|
|
13467
|
+
const dir = path50.join(paths.runtimeDir, "enforcement", "briefings");
|
|
13304
13468
|
await mkdir19(dir, { recursive: true });
|
|
13305
|
-
const file =
|
|
13469
|
+
const file = path50.join(dir, `${sessionId}.md`);
|
|
13306
13470
|
const parts = [
|
|
13307
13471
|
"# hAIve Briefing",
|
|
13308
13472
|
"",
|
|
@@ -13324,10 +13488,10 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13324
13488
|
return file;
|
|
13325
13489
|
}
|
|
13326
13490
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
13327
|
-
const root =
|
|
13328
|
-
const paths =
|
|
13329
|
-
const initialized =
|
|
13330
|
-
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) : {};
|
|
13331
13495
|
if (initialized) await applyLightweightRepairs(root, paths);
|
|
13332
13496
|
const mode = config.enforcement?.mode ?? "strict";
|
|
13333
13497
|
const findings = [];
|
|
@@ -13357,7 +13521,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13357
13521
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13358
13522
|
});
|
|
13359
13523
|
}
|
|
13360
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
13524
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.29"));
|
|
13361
13525
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13362
13526
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
13363
13527
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13427,7 +13591,7 @@ function withCategories(report) {
|
|
|
13427
13591
|
};
|
|
13428
13592
|
}
|
|
13429
13593
|
async function hasRecentSessionRecap(paths) {
|
|
13430
|
-
if (!
|
|
13594
|
+
if (!existsSync68(paths.memoriesDir)) return false;
|
|
13431
13595
|
const all = await loadMemoriesFromDir36(paths.memoriesDir);
|
|
13432
13596
|
return all.some(({ memory: memory2 }) => {
|
|
13433
13597
|
const fm = memory2.frontmatter;
|
|
@@ -13436,7 +13600,7 @@ async function hasRecentSessionRecap(paths) {
|
|
|
13436
13600
|
});
|
|
13437
13601
|
}
|
|
13438
13602
|
async function verifyMemoryPolicy(paths, config) {
|
|
13439
|
-
if (!
|
|
13603
|
+
if (!existsSync68(paths.memoriesDir)) return [];
|
|
13440
13604
|
const all = await loadMemoriesFromDir36(paths.memoriesDir);
|
|
13441
13605
|
const findings = [];
|
|
13442
13606
|
const staleImportant = [];
|
|
@@ -13474,7 +13638,7 @@ async function verifyMemoryPolicy(paths, config) {
|
|
|
13474
13638
|
return findings;
|
|
13475
13639
|
}
|
|
13476
13640
|
async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
13477
|
-
if (!
|
|
13641
|
+
if (!existsSync68(paths.memoriesDir)) return [];
|
|
13478
13642
|
const changedFiles = await getChangedFiles(paths.root, stage);
|
|
13479
13643
|
if (changedFiles.length === 0) {
|
|
13480
13644
|
return [{ severity: "info", code: "decision-coverage-no-changes", message: "No changed files to match against policy memories." }];
|
|
@@ -13573,16 +13737,16 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
13573
13737
|
for (const entry of entries) {
|
|
13574
13738
|
if (entry.name === ".gitignore" || entry.name === "README.md") continue;
|
|
13575
13739
|
if (entry.name === "enforcement") {
|
|
13576
|
-
removed += await cleanupEnforcementDir(
|
|
13740
|
+
removed += await cleanupEnforcementDir(path50.join(runtimeDir, entry.name));
|
|
13577
13741
|
continue;
|
|
13578
13742
|
}
|
|
13579
|
-
await rm3(
|
|
13743
|
+
await rm3(path50.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
13580
13744
|
removed++;
|
|
13581
13745
|
}
|
|
13582
|
-
await writeFile33(
|
|
13583
|
-
if (!
|
|
13746
|
+
await writeFile33(path50.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
13747
|
+
if (!existsSync68(path50.join(runtimeDir, "README.md"))) {
|
|
13584
13748
|
await writeFile33(
|
|
13585
|
-
|
|
13749
|
+
path50.join(runtimeDir, "README.md"),
|
|
13586
13750
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
13587
13751
|
"utf8"
|
|
13588
13752
|
);
|
|
@@ -13595,10 +13759,10 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
13595
13759
|
const entries = await readdir6(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
13596
13760
|
for (const entry of entries) {
|
|
13597
13761
|
if (entry.name === ".gitignore") continue;
|
|
13598
|
-
await rm3(
|
|
13762
|
+
await rm3(path50.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
13599
13763
|
removed++;
|
|
13600
13764
|
}
|
|
13601
|
-
await writeFile33(
|
|
13765
|
+
await writeFile33(path50.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
13602
13766
|
return removed;
|
|
13603
13767
|
}
|
|
13604
13768
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -13606,7 +13770,7 @@ async function cleanupEnforcementDir(enforcementDir) {
|
|
|
13606
13770
|
const entries = await readdir6(enforcementDir, { withFileTypes: true }).catch(() => []);
|
|
13607
13771
|
for (const entry of entries) {
|
|
13608
13772
|
if (entry.name === "briefings") continue;
|
|
13609
|
-
await rm3(
|
|
13773
|
+
await rm3(path50.join(enforcementDir, entry.name), { recursive: true, force: true });
|
|
13610
13774
|
removed++;
|
|
13611
13775
|
}
|
|
13612
13776
|
return removed;
|
|
@@ -13622,9 +13786,9 @@ async function inspectIntegrationVersions(root, expectedVersion) {
|
|
|
13622
13786
|
];
|
|
13623
13787
|
const findings = [];
|
|
13624
13788
|
for (const rel of files) {
|
|
13625
|
-
const file =
|
|
13626
|
-
if (!
|
|
13627
|
-
const text = await
|
|
13789
|
+
const file = path50.join(root, rel);
|
|
13790
|
+
if (!existsSync68(file)) continue;
|
|
13791
|
+
const text = await readFile21(file, "utf8").catch(() => "");
|
|
13628
13792
|
for (const bin of extractAbsoluteHaiveBins2(text)) {
|
|
13629
13793
|
const version = versionForBinary2(bin);
|
|
13630
13794
|
if (!version) {
|
|
@@ -13718,8 +13882,8 @@ function buildScore(findings, threshold = 80) {
|
|
|
13718
13882
|
};
|
|
13719
13883
|
}
|
|
13720
13884
|
async function installGitEnforcement(root) {
|
|
13721
|
-
const hooksDir =
|
|
13722
|
-
if (!
|
|
13885
|
+
const hooksDir = path50.join(root, ".git", "hooks");
|
|
13886
|
+
if (!existsSync68(path50.join(root, ".git"))) {
|
|
13723
13887
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
13724
13888
|
return;
|
|
13725
13889
|
}
|
|
@@ -13741,9 +13905,9 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
13741
13905
|
}
|
|
13742
13906
|
];
|
|
13743
13907
|
for (const hook of hooks) {
|
|
13744
|
-
const file =
|
|
13745
|
-
if (
|
|
13746
|
-
const current = await
|
|
13908
|
+
const file = path50.join(hooksDir, hook.name);
|
|
13909
|
+
if (existsSync68(file)) {
|
|
13910
|
+
const current = await readFile21(file, "utf8").catch(() => "");
|
|
13747
13911
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
13748
13912
|
await writeFile33(file, hook.body, "utf8");
|
|
13749
13913
|
} else {
|
|
@@ -13759,9 +13923,9 @@ ${hook.body}`, "utf8");
|
|
|
13759
13923
|
ui.success("Installed blocking git enforcement hooks: pre-commit, pre-push");
|
|
13760
13924
|
}
|
|
13761
13925
|
async function installCiEnforcement(root) {
|
|
13762
|
-
const workflowPath =
|
|
13763
|
-
await mkdir19(
|
|
13764
|
-
if (
|
|
13926
|
+
const workflowPath = path50.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
13927
|
+
await mkdir19(path50.dirname(workflowPath), { recursive: true });
|
|
13928
|
+
if (existsSync68(workflowPath)) {
|
|
13765
13929
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
13766
13930
|
return;
|
|
13767
13931
|
}
|
|
@@ -13789,7 +13953,7 @@ jobs:
|
|
|
13789
13953
|
- name: Enforce hAIve policy
|
|
13790
13954
|
run: haive enforce ci
|
|
13791
13955
|
`, "utf8");
|
|
13792
|
-
ui.success(`Created ${
|
|
13956
|
+
ui.success(`Created ${path50.relative(root, workflowPath)}`);
|
|
13793
13957
|
}
|
|
13794
13958
|
function printReport(report, json, explain = false) {
|
|
13795
13959
|
if (json) {
|
|
@@ -13849,7 +14013,7 @@ async function readHookPayload() {
|
|
|
13849
14013
|
}
|
|
13850
14014
|
function resolveRoot(dir, payload) {
|
|
13851
14015
|
try {
|
|
13852
|
-
return
|
|
14016
|
+
return findProjectRoot49(dir ?? payload.cwd);
|
|
13853
14017
|
} catch {
|
|
13854
14018
|
return null;
|
|
13855
14019
|
}
|
|
@@ -13886,11 +14050,11 @@ function extractToolPaths(payload, root) {
|
|
|
13886
14050
|
}
|
|
13887
14051
|
function normalizeToolPath(file, root) {
|
|
13888
14052
|
const normalized = file.replace(/\\/g, "/");
|
|
13889
|
-
if (!
|
|
13890
|
-
return
|
|
14053
|
+
if (!path50.isAbsolute(normalized)) return normalized.replace(/^\.\//, "");
|
|
14054
|
+
return path50.relative(root, normalized).replace(/\\/g, "/");
|
|
13891
14055
|
}
|
|
13892
14056
|
async function missingRequiredMemoriesForFiles(paths, files, sessionId) {
|
|
13893
|
-
if (!
|
|
14057
|
+
if (!existsSync68(paths.memoriesDir)) return [];
|
|
13894
14058
|
const marker = await readRecentBriefingMarker(paths, sessionId);
|
|
13895
14059
|
const consulted = new Set(marker?.memory_ids ?? []);
|
|
13896
14060
|
const policyTypes = /* @__PURE__ */ new Set(["decision", "gotcha", "architecture", "convention", "attempt"]);
|
|
@@ -13963,8 +14127,8 @@ function registerRun(program2) {
|
|
|
13963
14127
|
}
|
|
13964
14128
|
|
|
13965
14129
|
// src/index.ts
|
|
13966
|
-
var program = new
|
|
13967
|
-
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");
|
|
13968
14132
|
registerInit(program);
|
|
13969
14133
|
registerWelcome(program);
|
|
13970
14134
|
registerResolveProject(program);
|
|
@@ -13998,6 +14162,7 @@ registerMemoryApprove(memory);
|
|
|
13998
14162
|
registerMemoryUpdate(memory);
|
|
13999
14163
|
registerMemoryHot(memory);
|
|
14000
14164
|
registerMemoryTried(memory);
|
|
14165
|
+
registerMemorySeed(memory);
|
|
14001
14166
|
registerMemoryImport(memory);
|
|
14002
14167
|
registerMemoryImportChangelog(memory);
|
|
14003
14168
|
registerMemoryDigest(memory);
|