@hiveai/cli 0.9.26 → 0.9.28
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 +298 -131
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -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,
|
|
@@ -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";
|
|
@@ -4815,7 +4823,8 @@ var SessionTracker = class {
|
|
|
4815
4823
|
const ranPostTask = this.events.some(
|
|
4816
4824
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
4817
4825
|
);
|
|
4818
|
-
|
|
4826
|
+
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
4827
|
+
if (!ranPostTask && isSubstantialSession && existsSync16(this.ctx.paths.haiveDir)) {
|
|
4819
4828
|
try {
|
|
4820
4829
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
4821
4830
|
const payload = {
|
|
@@ -5426,6 +5435,16 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5426
5435
|
);
|
|
5427
5436
|
}
|
|
5428
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
|
+
}
|
|
5429
5448
|
return {
|
|
5430
5449
|
...input.task ? { task: input.task } : {},
|
|
5431
5450
|
search_mode: searchMode,
|
|
@@ -5480,6 +5499,9 @@ function classifyMemoryPriority(memory2, loaded, inputFiles, inputSymbols) {
|
|
|
5480
5499
|
if (fm?.requires_human_approval || directAnchor || directSymbol || memory2.type === "attempt" && (memory2.match_quality === "exact" || strongSemantic) || memory2.type === "skill" && (memory2.match_quality === "exact" || strongSemantic)) {
|
|
5481
5500
|
return "must_read";
|
|
5482
5501
|
}
|
|
5502
|
+
if (isStackPackSeed2(fm)) {
|
|
5503
|
+
return "background";
|
|
5504
|
+
}
|
|
5483
5505
|
if (memory2.type === "skill" || memory2.reasons.includes("module") || memory2.reasons.includes("domain") || memory2.match_quality === "exact" || usefulSemantic) {
|
|
5484
5506
|
return "useful";
|
|
5485
5507
|
}
|
|
@@ -5962,6 +5984,8 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
5962
5984
|
confidence: deriveConfidence6(fm, u),
|
|
5963
5985
|
body_preview: body.split("\n").slice(0, 5).join("\n").slice(0, 400),
|
|
5964
5986
|
reasons: [reason],
|
|
5987
|
+
tags: fm.tags ?? [],
|
|
5988
|
+
anchor_paths: fm.anchor?.paths ?? [],
|
|
5965
5989
|
...score !== void 0 ? { semantic_score: score } : {}
|
|
5966
5990
|
});
|
|
5967
5991
|
};
|
|
@@ -6552,8 +6576,45 @@ function fileTypeDowngradeReason(warning, paths) {
|
|
|
6552
6576
|
if (configOnly && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
|
|
6553
6577
|
return "package/config-only change; warning has no anchor on these files and no strong semantic match \u2014 downgraded to info.";
|
|
6554
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
|
+
}
|
|
6555
6583
|
return null;
|
|
6556
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
|
+
]);
|
|
6557
6618
|
function hasStrongSemantic(warning) {
|
|
6558
6619
|
return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
|
|
6559
6620
|
}
|
|
@@ -6564,9 +6625,36 @@ function isDocLikePath(file) {
|
|
|
6564
6625
|
function isPackageOrConfigPath(file) {
|
|
6565
6626
|
const lower = file.toLowerCase();
|
|
6566
6627
|
const base = lower.split("/").pop() ?? lower;
|
|
6567
|
-
return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") ||
|
|
6628
|
+
return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") || isJsonConfigFile(base) || lower.endsWith(".yml") || lower.endsWith(".yaml") || lower.endsWith(".toml") || lower.startsWith(".github/workflows/") || lower.startsWith(".github/") && lower.endsWith(".yml") || // Dotfiles that are pure configuration/tooling — never trigger runtime gotchas
|
|
6568
6629
|
base === ".gitignore" || base === ".gitattributes" || base === ".gitmodules" || base === ".editorconfig" || base === ".nvmrc" || base === ".node-version" || base === ".npmrc" || base === ".yarnrc" || base === ".yarnrc.yml" || base === ".dockerignore" || base === "dockerfile" || base.startsWith("dockerfile.") || base === ".env.example" || base === ".env.template" || lower.endsWith(".prettierrc") || lower.endsWith(".eslintrc") || lower.endsWith(".eslintignore") || lower.endsWith(".prettierignore") || lower.endsWith(".stylelintrc") || lower.endsWith(".browserslistrc");
|
|
6569
6630
|
}
|
|
6631
|
+
function isJsonConfigFile(base) {
|
|
6632
|
+
const knownConfigs = /* @__PURE__ */ new Set([
|
|
6633
|
+
"tsconfig.json",
|
|
6634
|
+
"jsconfig.json",
|
|
6635
|
+
"deno.json",
|
|
6636
|
+
"deno.jsonc",
|
|
6637
|
+
"nx.json",
|
|
6638
|
+
"turbo.json",
|
|
6639
|
+
"lerna.json",
|
|
6640
|
+
"rush.json",
|
|
6641
|
+
"jest.config.json",
|
|
6642
|
+
"vitest.config.json",
|
|
6643
|
+
"babel.config.json",
|
|
6644
|
+
".babelrc.json",
|
|
6645
|
+
".swcrc",
|
|
6646
|
+
".mocharc.json",
|
|
6647
|
+
"renovate.json",
|
|
6648
|
+
"dependabot.json",
|
|
6649
|
+
".prettierrc.json",
|
|
6650
|
+
".eslintrc.json",
|
|
6651
|
+
".stylelintrc.json"
|
|
6652
|
+
]);
|
|
6653
|
+
if (knownConfigs.has(base)) return true;
|
|
6654
|
+
if (/^tsconfig\..+\.json$/.test(base)) return true;
|
|
6655
|
+
if (/^\.[a-z]+rc\.json$/.test(base)) return true;
|
|
6656
|
+
return false;
|
|
6657
|
+
}
|
|
6570
6658
|
function repairCommandForWarning(warning, paths) {
|
|
6571
6659
|
const firstPath = paths[0];
|
|
6572
6660
|
return firstPath ? `haive briefing --files "${firstPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
|
|
@@ -7102,7 +7190,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7102
7190
|
};
|
|
7103
7191
|
}
|
|
7104
7192
|
var SERVER_NAME = "haive";
|
|
7105
|
-
var SERVER_VERSION = "0.9.
|
|
7193
|
+
var SERVER_VERSION = "0.9.28";
|
|
7106
7194
|
function jsonResult(data) {
|
|
7107
7195
|
return {
|
|
7108
7196
|
content: [
|
|
@@ -8083,6 +8171,7 @@ import {
|
|
|
8083
8171
|
getUsage as getUsage10,
|
|
8084
8172
|
isAutoPromoteEligible as isAutoPromoteEligible2,
|
|
8085
8173
|
isDecaying as isDecaying2,
|
|
8174
|
+
isStackPackSeed as isStackPackSeed3,
|
|
8086
8175
|
loadCodeMap as loadCodeMap5,
|
|
8087
8176
|
loadConfig as loadConfig4,
|
|
8088
8177
|
loadMemoriesFromDir as loadMemoriesFromDir23,
|
|
@@ -8099,14 +8188,14 @@ var BRIDGE_START = "<!-- haive:memories-start -->";
|
|
|
8099
8188
|
var BRIDGE_END = "<!-- haive:memories-end -->";
|
|
8100
8189
|
function registerSync(program2) {
|
|
8101
8190
|
program2.command("sync").description(
|
|
8102
|
-
"Refresh memory state after a git pull or merge.\n What it does:\n 1. Verifies anchor paths \u2014 marks stale if files/symbols moved or deleted\n 2. Re-validates previously stale memories that are now fresh\n 3. Auto-promotes proposed memories (by usage count or time delay in autopilot)\n 4. Auto-refreshes code-map if source files changed\n 5. Reports decay warnings for memories unused >90 days\n\n Install git hooks to run sync automatically: haive install-hooks\n\n Examples:\n haive sync\n haive sync --since main # also report memories changed since main\n haive sync --embed # also rebuild embeddings index\n"
|
|
8191
|
+
"Refresh memory state after a git pull or merge.\n What it does:\n 1. Verifies anchor paths \u2014 marks stale if files/symbols moved or deleted\n 2. Re-validates previously stale memories that are now fresh\n 3. Auto-promotes proposed memories (by usage count or time delay in autopilot)\n 4. Auto-refreshes code-map if source files changed\n 5. Reports decay warnings for memories unused >90 days\n\n Install git hooks to run sync automatically: haive install-hooks\n\n Examples:\n haive sync\n haive sync --dry-run # preview what would change without writing\n haive sync --since main # also report memories changed since main\n haive sync --embed # also rebuild embeddings index\n"
|
|
8103
8192
|
).option("-d, --dir <dir>", "project root").option("--quiet", "minimal output (suitable for git hooks)").option(
|
|
8104
8193
|
"--since <ref>",
|
|
8105
8194
|
"git ref/commit to compare against; report memories added/modified/removed since"
|
|
8106
8195
|
).option("--no-verify", "skip the anchor verification step").option("--no-promote", "skip the auto-promotion step").option(
|
|
8107
8196
|
"--inject-bridge",
|
|
8108
8197
|
"inject top validated memories into CLAUDE.md (or --bridge-file) between <!-- haive:memories-start/end --> markers"
|
|
8109
|
-
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").action(async (opts) => {
|
|
8198
|
+
).option("--bridge-file <path>", "bridge file to inject into (default: CLAUDE.md)").option("--bridge-max-memories <n>", "max memories to inject into bridge file", "5").option("--embed", "rebuild embeddings index after sync (requires @haive/embeddings)").option("--no-cross-repo", "skip cross-repo memory pull even if crossRepoSources is configured").option("--no-deps", "skip dependency version tracking").option("--no-contracts", "skip contract file diff checking").option("--dry-run", "report what would change without writing any files").action(async (opts) => {
|
|
8110
8199
|
const root = findProjectRoot12(opts.dir);
|
|
8111
8200
|
const paths = resolveHaivePaths9(root);
|
|
8112
8201
|
if (!existsSync29(paths.memoriesDir)) {
|
|
@@ -8121,6 +8210,8 @@ function registerSync(program2) {
|
|
|
8121
8210
|
const autoApproveDelayHours = config.autoApproveDelayHours ?? null;
|
|
8122
8211
|
const autoPromoteMinReads = config.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE2.minReads;
|
|
8123
8212
|
const autoRepair = config.autoRepair ?? {};
|
|
8213
|
+
const dryRun = opts.dryRun === true;
|
|
8214
|
+
if (dryRun) log(ui.yellow("(dry run \u2014 no files will be written)"));
|
|
8124
8215
|
let staleMarked = 0;
|
|
8125
8216
|
let revalidated = 0;
|
|
8126
8217
|
let promoted = 0;
|
|
@@ -8130,19 +8221,21 @@ function registerSync(program2) {
|
|
|
8130
8221
|
for (const { memory: memory2, filePath } of memories) {
|
|
8131
8222
|
if (memory2.frontmatter.type === "session_recap") {
|
|
8132
8223
|
if (memory2.frontmatter.status === "stale") {
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8224
|
+
if (!dryRun) {
|
|
8225
|
+
await writeFile13(
|
|
8226
|
+
filePath,
|
|
8227
|
+
serializeMemory11({
|
|
8228
|
+
frontmatter: {
|
|
8229
|
+
...memory2.frontmatter,
|
|
8230
|
+
status: "validated",
|
|
8231
|
+
stale_reason: null,
|
|
8232
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8233
|
+
},
|
|
8234
|
+
body: memory2.body
|
|
8235
|
+
}),
|
|
8236
|
+
"utf8"
|
|
8237
|
+
);
|
|
8238
|
+
}
|
|
8146
8239
|
revalidated++;
|
|
8147
8240
|
}
|
|
8148
8241
|
continue;
|
|
@@ -8153,35 +8246,39 @@ function registerSync(program2) {
|
|
|
8153
8246
|
const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8154
8247
|
if (result.stale) {
|
|
8155
8248
|
if (memory2.frontmatter.status !== "stale") {
|
|
8249
|
+
if (!dryRun) {
|
|
8250
|
+
await writeFile13(
|
|
8251
|
+
filePath,
|
|
8252
|
+
serializeMemory11({
|
|
8253
|
+
frontmatter: {
|
|
8254
|
+
...memory2.frontmatter,
|
|
8255
|
+
status: "stale",
|
|
8256
|
+
verified_at: verifiedAt,
|
|
8257
|
+
stale_reason: result.reason
|
|
8258
|
+
},
|
|
8259
|
+
body: memory2.body
|
|
8260
|
+
}),
|
|
8261
|
+
"utf8"
|
|
8262
|
+
);
|
|
8263
|
+
}
|
|
8264
|
+
staleMarked++;
|
|
8265
|
+
}
|
|
8266
|
+
} else if (memory2.frontmatter.status === "stale") {
|
|
8267
|
+
if (!dryRun) {
|
|
8156
8268
|
await writeFile13(
|
|
8157
8269
|
filePath,
|
|
8158
8270
|
serializeMemory11({
|
|
8159
8271
|
frontmatter: {
|
|
8160
8272
|
...memory2.frontmatter,
|
|
8161
|
-
status: "
|
|
8273
|
+
status: "validated",
|
|
8162
8274
|
verified_at: verifiedAt,
|
|
8163
|
-
stale_reason:
|
|
8275
|
+
stale_reason: null
|
|
8164
8276
|
},
|
|
8165
8277
|
body: memory2.body
|
|
8166
8278
|
}),
|
|
8167
8279
|
"utf8"
|
|
8168
8280
|
);
|
|
8169
|
-
staleMarked++;
|
|
8170
8281
|
}
|
|
8171
|
-
} else if (memory2.frontmatter.status === "stale") {
|
|
8172
|
-
await writeFile13(
|
|
8173
|
-
filePath,
|
|
8174
|
-
serializeMemory11({
|
|
8175
|
-
frontmatter: {
|
|
8176
|
-
...memory2.frontmatter,
|
|
8177
|
-
status: "validated",
|
|
8178
|
-
verified_at: verifiedAt,
|
|
8179
|
-
stale_reason: null
|
|
8180
|
-
},
|
|
8181
|
-
body: memory2.body
|
|
8182
|
-
}),
|
|
8183
|
-
"utf8"
|
|
8184
|
-
);
|
|
8185
8282
|
revalidated++;
|
|
8186
8283
|
}
|
|
8187
8284
|
}
|
|
@@ -8197,35 +8294,39 @@ function registerSync(program2) {
|
|
|
8197
8294
|
minReads: autoPromoteMinReads,
|
|
8198
8295
|
maxRejections: DEFAULT_AUTO_PROMOTE_RULE2.maxRejections
|
|
8199
8296
|
})) {
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8297
|
+
if (!dryRun) {
|
|
8298
|
+
await writeFile13(
|
|
8299
|
+
filePath,
|
|
8300
|
+
serializeMemory11({ frontmatter: { ...fm, status: "validated" }, body: memory2.body }),
|
|
8301
|
+
"utf8"
|
|
8302
|
+
);
|
|
8303
|
+
}
|
|
8205
8304
|
promoted++;
|
|
8206
8305
|
continue;
|
|
8207
8306
|
}
|
|
8208
8307
|
if (autoApproveDelayHours !== null && fm.status === "proposed" && fm.scope === "team") {
|
|
8209
8308
|
const ageHours = (nowMs - new Date(fm.created_at).getTime()) / (1e3 * 60 * 60);
|
|
8210
8309
|
if (ageHours >= autoApproveDelayHours) {
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8310
|
+
if (!dryRun) {
|
|
8311
|
+
await writeFile13(
|
|
8312
|
+
filePath,
|
|
8313
|
+
serializeMemory11({
|
|
8314
|
+
frontmatter: {
|
|
8315
|
+
...fm,
|
|
8316
|
+
status: "validated",
|
|
8317
|
+
verified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8318
|
+
},
|
|
8319
|
+
body: memory2.body
|
|
8320
|
+
}),
|
|
8321
|
+
"utf8"
|
|
8322
|
+
);
|
|
8323
|
+
}
|
|
8223
8324
|
autoApproved++;
|
|
8224
8325
|
}
|
|
8225
8326
|
}
|
|
8226
8327
|
}
|
|
8227
8328
|
}
|
|
8228
|
-
if (config.autopilot || autoRepair.context || autoRepair.corpus) {
|
|
8329
|
+
if (!dryRun && (config.autopilot || autoRepair.context || autoRepair.corpus)) {
|
|
8229
8330
|
const repairs = await applyAutopilotRepairs(root, paths, {
|
|
8230
8331
|
applyContext: autoRepair.context ?? config.autopilot,
|
|
8231
8332
|
applyCorpus: autoRepair.corpus ?? config.autopilot,
|
|
@@ -8357,14 +8458,16 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8357
8458
|
paths: [result.file],
|
|
8358
8459
|
topic: `dep-bump-${slugParts}`
|
|
8359
8460
|
});
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8461
|
+
if (!dryRun) {
|
|
8462
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
8463
|
+
await mkdir10(teamDir, { recursive: true });
|
|
8464
|
+
await writeFile13(
|
|
8465
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
8466
|
+
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
8467
|
+
"utf8"
|
|
8468
|
+
);
|
|
8469
|
+
}
|
|
8470
|
+
log(ui.yellow(` \u2192 memory${dryRun ? " would be" : ""} created: ${fm.id}`));
|
|
8368
8471
|
}
|
|
8369
8472
|
}
|
|
8370
8473
|
}
|
|
@@ -8424,14 +8527,16 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8424
8527
|
paths: [diff.file],
|
|
8425
8528
|
topic: `contract-breaking-${diff.contract}`
|
|
8426
8529
|
});
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8530
|
+
if (!dryRun) {
|
|
8531
|
+
const teamDir = path15.join(paths.memoriesDir, "team");
|
|
8532
|
+
await mkdir10(teamDir, { recursive: true });
|
|
8533
|
+
await writeFile13(
|
|
8534
|
+
path15.join(teamDir, `${fm.id}.md`),
|
|
8535
|
+
serializeMemory11({ frontmatter: { ...fm, requires_human_approval: true }, body }),
|
|
8536
|
+
"utf8"
|
|
8537
|
+
);
|
|
8538
|
+
}
|
|
8539
|
+
log(ui.yellow(` \u2192 memory${dryRun ? " would be" : ""} created: ${fm.id}`));
|
|
8435
8540
|
}
|
|
8436
8541
|
}
|
|
8437
8542
|
} catch (err) {
|
|
@@ -8439,7 +8544,7 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8439
8544
|
}
|
|
8440
8545
|
}
|
|
8441
8546
|
const existingMap = await loadCodeMap5(paths);
|
|
8442
|
-
if (!existingMap && (config.autopilot || autoRepair.codeMap)) {
|
|
8547
|
+
if (!dryRun && !existingMap && (config.autopilot || autoRepair.codeMap)) {
|
|
8443
8548
|
try {
|
|
8444
8549
|
const { buildCodeMap: buildCodeMap4, saveCodeMap: saveCodeMap4 } = await import("@hiveai/core");
|
|
8445
8550
|
log(ui.dim("code-map: missing \u2014 building index\u2026"));
|
|
@@ -8474,17 +8579,21 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8474
8579
|
);
|
|
8475
8580
|
const changedSourceFiles = (gitResult.stdout ?? "").trim();
|
|
8476
8581
|
if (changedSourceFiles.length > 0) {
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8582
|
+
if (!dryRun) {
|
|
8583
|
+
try {
|
|
8584
|
+
const { buildCodeMap: buildCodeMap4, saveCodeMap: saveCodeMap4 } = await import("@hiveai/core");
|
|
8585
|
+
log(ui.dim("code-map: source files changed \u2014 refreshing index\u2026"));
|
|
8586
|
+
const newMap = await buildCodeMap4(root);
|
|
8587
|
+
await saveCodeMap4(paths, newMap);
|
|
8588
|
+
log(ui.dim(`code-map: refreshed (${Object.keys(newMap.files).length} files)`));
|
|
8589
|
+
} catch {
|
|
8590
|
+
}
|
|
8591
|
+
} else {
|
|
8592
|
+
log(ui.dim("code-map: source files changed \u2014 would refresh index (skipped in dry-run)"));
|
|
8484
8593
|
}
|
|
8485
8594
|
}
|
|
8486
8595
|
}
|
|
8487
|
-
if (opts.embed || autoRepair.codeSearch) {
|
|
8596
|
+
if (!dryRun && (opts.embed || autoRepair.codeSearch)) {
|
|
8488
8597
|
try {
|
|
8489
8598
|
const { Embedder, rebuildCodeIndex, rebuildIndex } = await import("@hiveai/embeddings");
|
|
8490
8599
|
log(ui.dim("embed: rebuilding index\u2026"));
|
|
@@ -8507,12 +8616,18 @@ Attends une **confirmation explicite** avant d'agir.
|
|
|
8507
8616
|
}
|
|
8508
8617
|
});
|
|
8509
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
|
+
}
|
|
8510
8624
|
async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
8511
8625
|
if (!existsSync29(memoriesDir)) return;
|
|
8512
8626
|
const all = await loadMemoriesFromDir23(memoriesDir);
|
|
8513
8627
|
const top = all.filter(({ memory: memory2 }) => {
|
|
8514
8628
|
const s = memory2.frontmatter.status;
|
|
8515
8629
|
if (memory2.frontmatter.type === "session_recap") return false;
|
|
8630
|
+
if (isStackPackSeed3(memory2.frontmatter)) return false;
|
|
8516
8631
|
return s === "validated" || s === "proposed";
|
|
8517
8632
|
}).sort((a, b) => {
|
|
8518
8633
|
const score = (m) => {
|
|
@@ -8524,11 +8639,11 @@ async function injectBridge(bridgeFile, memoriesDir, maxMemories, root, quiet) {
|
|
|
8524
8639
|
const block = top.map((m) => {
|
|
8525
8640
|
const fm = m.memory.frontmatter;
|
|
8526
8641
|
const unverified = fm.status === "proposed" ? " [UNVERIFIED]" : "";
|
|
8527
|
-
return
|
|
8528
|
-
|
|
8529
|
-
}).join("\n\n---\n\n");
|
|
8642
|
+
return `- \`${fm.id}\` (${fm.scope}/${fm.type})${unverified} \u2014 ${bridgeSummaryLine(m.memory.body)}`;
|
|
8643
|
+
}).join("\n");
|
|
8530
8644
|
const injected = `${BRIDGE_START}
|
|
8531
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. -->
|
|
8532
8647
|
|
|
8533
8648
|
` + block + `
|
|
8534
8649
|
|
|
@@ -8824,7 +8939,7 @@ import {
|
|
|
8824
8939
|
|
|
8825
8940
|
// src/commands/memory-list.ts
|
|
8826
8941
|
function registerMemoryList(memory2) {
|
|
8827
|
-
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8942
|
+
memory2.command("list").description("List memories with optional filters").option("--scope <scope>", "personal | team | module").option("--type <type>", "filter by type").option("--tag <tag>", "filter by tag").option("--module <name>", "filter by module name").option("--status <csv>", "filter by status (draft,proposed,validated,stale,rejected,deprecated)").option("--show-rejected", "include rejected memories (hidden by default)").option("--limit <n>", "max memories to display").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8828
8943
|
const root = findProjectRoot14(opts.dir);
|
|
8829
8944
|
const paths = resolveHaivePaths11(root);
|
|
8830
8945
|
if (!existsSync31(paths.memoriesDir)) {
|
|
@@ -8834,6 +8949,7 @@ function registerMemoryList(memory2) {
|
|
|
8834
8949
|
}
|
|
8835
8950
|
const all = await loadMemoriesFromDir25(paths.memoriesDir);
|
|
8836
8951
|
const statusFilter = opts.status ? opts.status.split(",").map((s) => s.trim()) : null;
|
|
8952
|
+
const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10)) : void 0;
|
|
8837
8953
|
const filtered = all.filter((m) => {
|
|
8838
8954
|
if (!matchesFilters(m, opts)) return false;
|
|
8839
8955
|
const status = m.memory.frontmatter.status;
|
|
@@ -8851,7 +8967,9 @@ function registerMemoryList(memory2) {
|
|
|
8851
8967
|
}
|
|
8852
8968
|
return;
|
|
8853
8969
|
}
|
|
8854
|
-
|
|
8970
|
+
const displayed = limit !== void 0 ? filtered.slice(0, limit) : filtered;
|
|
8971
|
+
const clipped = filtered.length - displayed.length;
|
|
8972
|
+
for (const { memory: mem, filePath } of displayed) {
|
|
8855
8973
|
const fm = mem.frontmatter;
|
|
8856
8974
|
const tagStr = fm.tags.length ? ui.dim(` [${fm.tags.join(", ")}]`) : "";
|
|
8857
8975
|
const moduleStr = fm.module ? ui.dim(` (${fm.module})`) : "";
|
|
@@ -8863,8 +8981,10 @@ function registerMemoryList(memory2) {
|
|
|
8863
8981
|
if (title && title !== fm.id) console.log(` ${title}`);
|
|
8864
8982
|
console.log(` ${ui.dim(path17.relative(root, filePath))}`);
|
|
8865
8983
|
}
|
|
8866
|
-
|
|
8867
|
-
${
|
|
8984
|
+
const totalLabel = clipped > 0 ? `
|
|
8985
|
+
${displayed.length} of ${filtered.length} memories shown (use --limit to adjust)` : `
|
|
8986
|
+
${filtered.length} memor${filtered.length === 1 ? "y" : "ies"}`;
|
|
8987
|
+
console.log(ui.dim(totalLabel));
|
|
8868
8988
|
if (hiddenRejectedCount > 0) {
|
|
8869
8989
|
console.log(
|
|
8870
8990
|
ui.dim(`(${hiddenRejectedCount} rejected hidden \u2014 use --show-rejected to include)`)
|
|
@@ -9380,7 +9500,9 @@ import {
|
|
|
9380
9500
|
resolveHaivePaths as resolveHaivePaths18
|
|
9381
9501
|
} from "@hiveai/core";
|
|
9382
9502
|
function registerMemoryHot(memory2) {
|
|
9383
|
-
memory2.command("hot").description(
|
|
9503
|
+
memory2.command("hot").description(
|
|
9504
|
+
"List unvalidated memories with high read_count \u2014 proven-useful promotion candidates.\n\n Unlike `haive memory pending` (which lists ALL draft/proposed by status),\n `hot` filters by usage: only memories read \u2265N times qualify.\n Use it to quickly find memories that agents are already relying on\n but that haven't been formally validated yet."
|
|
9505
|
+
).option("--threshold <n>", "minimum read_count to qualify (default: 3)", "3").option("--status <status>", "limit to one status (default: draft + proposed)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
9384
9506
|
const root = findProjectRoot21(opts.dir);
|
|
9385
9507
|
const paths = resolveHaivePaths18(root);
|
|
9386
9508
|
if (!existsSync39(paths.memoriesDir)) {
|
|
@@ -9414,7 +9536,8 @@ function registerMemoryHot(memory2) {
|
|
|
9414
9536
|
console.log(` ${ui.dim(path25.relative(root, filePath))}`);
|
|
9415
9537
|
}
|
|
9416
9538
|
ui.info(
|
|
9417
|
-
`${candidates.length} hot \u2014
|
|
9539
|
+
`${candidates.length} hot (read \u2265${threshold}\xD7) \u2014 agents rely on these; promote with \`haive memory promote <id>\`.
|
|
9540
|
+
Tip: \`haive memory pending\` lists ALL unvalidated memories regardless of read count.`
|
|
9418
9541
|
);
|
|
9419
9542
|
});
|
|
9420
9543
|
}
|
|
@@ -11763,7 +11886,7 @@ function parseDays(input) {
|
|
|
11763
11886
|
|
|
11764
11887
|
// src/commands/doctor.ts
|
|
11765
11888
|
import { existsSync as existsSync60, statSync } from "fs";
|
|
11766
|
-
import { readFile as readFile19, stat } from "fs/promises";
|
|
11889
|
+
import { readFile as readFile19, stat, writeFile as writeFile31 } from "fs/promises";
|
|
11767
11890
|
import path44 from "path";
|
|
11768
11891
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
11769
11892
|
import "commander";
|
|
@@ -12007,26 +12130,58 @@ function registerDoctor(program2) {
|
|
|
12007
12130
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12008
12131
|
});
|
|
12009
12132
|
}
|
|
12010
|
-
findings.push(...await collectInstallFindings(root, "0.9.
|
|
12133
|
+
findings.push(...await collectInstallFindings(root, "0.9.28"));
|
|
12011
12134
|
try {
|
|
12012
12135
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12013
12136
|
encoding: "utf8",
|
|
12014
12137
|
timeout: 3e3,
|
|
12015
12138
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12016
12139
|
}).trim();
|
|
12017
|
-
const cliVersion = "0.9.
|
|
12140
|
+
const cliVersion = "0.9.28";
|
|
12018
12141
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12019
12142
|
findings.push({
|
|
12020
12143
|
severity: "warn",
|
|
12021
12144
|
code: "legacy-haive-mcp-stale",
|
|
12022
|
-
message: `Standalone haive-mcp on PATH is v${legacyRaw} but haive CLI is v${cliVersion}.
|
|
12023
|
-
fix:
|
|
12024
|
-
|
|
12145
|
+
message: `Standalone haive-mcp on PATH is v${legacyRaw} but haive CLI is v${cliVersion}. MCP is now bundled in haive itself \u2014 switch your client configs to command "haive" + args ["mcp", "--stdio"], then uninstall @hiveai/mcp.`,
|
|
12146
|
+
fix: `# 1. Run haive init to regenerate MCP configs pointing to bundled server:
|
|
12147
|
+
haive init
|
|
12148
|
+
# 2. Optionally remove the now-redundant standalone package:
|
|
12025
12149
|
npm uninstall -g @hiveai/mcp`
|
|
12026
12150
|
});
|
|
12027
12151
|
}
|
|
12028
12152
|
} catch {
|
|
12029
12153
|
}
|
|
12154
|
+
{
|
|
12155
|
+
const configPaths = [
|
|
12156
|
+
path44.join(root, ".mcp.json"),
|
|
12157
|
+
path44.join(root, ".cursor", "mcp.json"),
|
|
12158
|
+
path44.join(root, ".vscode", "mcp.json")
|
|
12159
|
+
];
|
|
12160
|
+
const staleConfigs = [];
|
|
12161
|
+
for (const cfgPath of configPaths) {
|
|
12162
|
+
if (!existsSync60(cfgPath)) continue;
|
|
12163
|
+
try {
|
|
12164
|
+
const raw = await readFile19(cfgPath, "utf8");
|
|
12165
|
+
if (raw.includes('"haive-mcp"') || raw.includes("'haive-mcp'")) {
|
|
12166
|
+
staleConfigs.push(path44.relative(root, cfgPath));
|
|
12167
|
+
if (opts.fix && !opts.dryRun) {
|
|
12168
|
+
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "haive"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
12169
|
+
await writeFile31(cfgPath, updated, "utf8");
|
|
12170
|
+
}
|
|
12171
|
+
}
|
|
12172
|
+
} catch {
|
|
12173
|
+
}
|
|
12174
|
+
}
|
|
12175
|
+
if (staleConfigs.length > 0) {
|
|
12176
|
+
findings.push({
|
|
12177
|
+
severity: "warn",
|
|
12178
|
+
code: "legacy-mcp-config",
|
|
12179
|
+
message: `${staleConfigs.length} MCP config file${staleConfigs.length === 1 ? "" : "s"} still reference the old "haive-mcp" command: ` + staleConfigs.join(", ") + `. Run \`haive doctor --fix\` to auto-migrate to the bundled server.`,
|
|
12180
|
+
fix: "haive doctor --fix",
|
|
12181
|
+
section: "Protection"
|
|
12182
|
+
});
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12030
12185
|
if (repairs.length > 0) {
|
|
12031
12186
|
findings.push({
|
|
12032
12187
|
severity: "info",
|
|
@@ -12150,9 +12305,21 @@ function groupBySection(findings) {
|
|
|
12150
12305
|
function nextActions(findings) {
|
|
12151
12306
|
return [...new Set(findings.flatMap((finding) => finding.fix ? finding.fix.split("\n") : []))].filter(Boolean);
|
|
12152
12307
|
}
|
|
12308
|
+
function isLowValueCoverageFile(file) {
|
|
12309
|
+
const lower = file.toLowerCase();
|
|
12310
|
+
const base = lower.split("/").pop() ?? lower;
|
|
12311
|
+
if (lower.includes(".test.") || lower.includes(".spec.")) return true;
|
|
12312
|
+
if (lower.includes("/__tests__/") || lower.includes("/test/") || lower.includes("/tests/")) return true;
|
|
12313
|
+
if (lower.endsWith(".d.ts")) return true;
|
|
12314
|
+
if (base === "tsup.config.ts" || base === "vitest.config.ts" || base === "jest.config.ts") return true;
|
|
12315
|
+
if (base.endsWith(".config.ts") || base.endsWith(".config.js")) return true;
|
|
12316
|
+
if (base === "vite.config.ts" || base === "vite.config.js") return true;
|
|
12317
|
+
return false;
|
|
12318
|
+
}
|
|
12153
12319
|
async function collectHarnessCoverageFindings(codeMap, memories) {
|
|
12154
12320
|
if (!codeMap) return [];
|
|
12155
|
-
const
|
|
12321
|
+
const allFiles = Object.keys(codeMap.files);
|
|
12322
|
+
const codeFiles = allFiles.filter((f) => !isLowValueCoverageFile(f));
|
|
12156
12323
|
const total = codeFiles.length;
|
|
12157
12324
|
if (total === 0) return [];
|
|
12158
12325
|
const validatedWithAnchors = memories.filter(
|
|
@@ -12926,7 +13093,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
12926
13093
|
// src/commands/enforce.ts
|
|
12927
13094
|
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
12928
13095
|
import { existsSync as existsSync67, statSync as statSync2 } from "fs";
|
|
12929
|
-
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as
|
|
13096
|
+
import { chmod as chmod2, mkdir as mkdir19, readFile as readFile20, readdir as readdir6, rm as rm3, writeFile as writeFile33 } from "fs/promises";
|
|
12930
13097
|
import path49 from "path";
|
|
12931
13098
|
import "commander";
|
|
12932
13099
|
import {
|
|
@@ -12942,7 +13109,7 @@ import {
|
|
|
12942
13109
|
saveConfig as saveConfig4,
|
|
12943
13110
|
SESSION_RECAP_TTL_MS,
|
|
12944
13111
|
verifyAnchor as verifyAnchor4,
|
|
12945
|
-
writeBriefingMarker as
|
|
13112
|
+
writeBriefingMarker as writeBriefingMarker3
|
|
12946
13113
|
} from "@hiveai/core";
|
|
12947
13114
|
var MAX_STDIN_BYTES2 = 256 * 1024;
|
|
12948
13115
|
var ENFORCE_HOOK_MARKER = "# hAIve enforcement hook";
|
|
@@ -13052,7 +13219,7 @@ function registerEnforce(program2) {
|
|
|
13052
13219
|
},
|
|
13053
13220
|
{ paths }
|
|
13054
13221
|
);
|
|
13055
|
-
await
|
|
13222
|
+
await writeBriefingMarker3(paths, {
|
|
13056
13223
|
sessionId,
|
|
13057
13224
|
task,
|
|
13058
13225
|
source: opts.source ?? "claude-session-start",
|
|
@@ -13137,7 +13304,7 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
13137
13304
|
}
|
|
13138
13305
|
const sessionId = `haive-run-${process.pid}-${Date.now()}`;
|
|
13139
13306
|
const task = opts.task ?? `Run agent command: ${[command, ...args].join(" ")}`;
|
|
13140
|
-
await
|
|
13307
|
+
await writeBriefingMarker3(paths, {
|
|
13141
13308
|
sessionId,
|
|
13142
13309
|
task,
|
|
13143
13310
|
source: "haive-run"
|
|
@@ -13194,7 +13361,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13194
13361
|
min_semantic_score: 0.25,
|
|
13195
13362
|
budget_preset: "quick"
|
|
13196
13363
|
}, { paths });
|
|
13197
|
-
await
|
|
13364
|
+
await writeBriefingMarker3(paths, {
|
|
13198
13365
|
sessionId,
|
|
13199
13366
|
task,
|
|
13200
13367
|
source: "haive-run",
|
|
@@ -13220,7 +13387,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
13220
13387
|
if (briefing.setup_warnings.length > 0) {
|
|
13221
13388
|
parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
|
|
13222
13389
|
}
|
|
13223
|
-
await
|
|
13390
|
+
await writeFile33(file, parts.join("\n") + "\n", "utf8");
|
|
13224
13391
|
return file;
|
|
13225
13392
|
}
|
|
13226
13393
|
async function buildEnforcementReport(dir, stage, sessionId) {
|
|
@@ -13257,7 +13424,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13257
13424
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13258
13425
|
});
|
|
13259
13426
|
}
|
|
13260
|
-
findings.push(...await inspectIntegrationVersions(root, "0.9.
|
|
13427
|
+
findings.push(...await inspectIntegrationVersions(root, "0.9.28"));
|
|
13261
13428
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13262
13429
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
13263
13430
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13479,9 +13646,9 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
13479
13646
|
await rm3(path49.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
13480
13647
|
removed++;
|
|
13481
13648
|
}
|
|
13482
|
-
await
|
|
13649
|
+
await writeFile33(path49.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
13483
13650
|
if (!existsSync67(path49.join(runtimeDir, "README.md"))) {
|
|
13484
|
-
await
|
|
13651
|
+
await writeFile33(
|
|
13485
13652
|
path49.join(runtimeDir, "README.md"),
|
|
13486
13653
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. hAIve cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
13487
13654
|
"utf8"
|
|
@@ -13498,7 +13665,7 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
13498
13665
|
await rm3(path49.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
13499
13666
|
removed++;
|
|
13500
13667
|
}
|
|
13501
|
-
await
|
|
13668
|
+
await writeFile33(path49.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
13502
13669
|
return removed;
|
|
13503
13670
|
}
|
|
13504
13671
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -13645,14 +13812,14 @@ haive enforce check --stage pre-push --dir . || exit $?
|
|
|
13645
13812
|
if (existsSync67(file)) {
|
|
13646
13813
|
const current = await readFile20(file, "utf8").catch(() => "");
|
|
13647
13814
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
13648
|
-
await
|
|
13815
|
+
await writeFile33(file, hook.body, "utf8");
|
|
13649
13816
|
} else {
|
|
13650
|
-
await
|
|
13817
|
+
await writeFile33(file, `${current.trimEnd()}
|
|
13651
13818
|
|
|
13652
13819
|
${hook.body}`, "utf8");
|
|
13653
13820
|
}
|
|
13654
13821
|
} else {
|
|
13655
|
-
await
|
|
13822
|
+
await writeFile33(file, hook.body, "utf8");
|
|
13656
13823
|
}
|
|
13657
13824
|
await chmod2(file, 493);
|
|
13658
13825
|
}
|
|
@@ -13665,7 +13832,7 @@ async function installCiEnforcement(root) {
|
|
|
13665
13832
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
13666
13833
|
return;
|
|
13667
13834
|
}
|
|
13668
|
-
await
|
|
13835
|
+
await writeFile33(workflowPath, `name: haive-enforcement
|
|
13669
13836
|
|
|
13670
13837
|
on:
|
|
13671
13838
|
pull_request:
|
|
@@ -13864,7 +14031,7 @@ function registerRun(program2) {
|
|
|
13864
14031
|
|
|
13865
14032
|
// src/index.ts
|
|
13866
14033
|
var program = new Command51();
|
|
13867
|
-
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.
|
|
14034
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.9.28").option("--advanced", "show maintenance and experimental commands in help");
|
|
13868
14035
|
registerInit(program);
|
|
13869
14036
|
registerWelcome(program);
|
|
13870
14037
|
registerResolveProject(program);
|