@hiveai/cli 0.9.30 → 0.10.0
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 +93 -36
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -253,7 +253,8 @@ import {
|
|
|
253
253
|
loadMemoriesFromDir,
|
|
254
254
|
loadUsageIndex,
|
|
255
255
|
resolveHaivePaths,
|
|
256
|
-
serializeMemory
|
|
256
|
+
serializeMemory,
|
|
257
|
+
specificityScore
|
|
257
258
|
} from "@hiveai/core";
|
|
258
259
|
async function lintMemoriesAsync(root, options = {}) {
|
|
259
260
|
const paths = resolveHaivePaths(root);
|
|
@@ -289,6 +290,15 @@ async function lintMemoriesAsync(root, options = {}) {
|
|
|
289
290
|
message: "Record does not contain obvious action/rationale words. Add the concrete rule, why it exists, and what to do instead."
|
|
290
291
|
});
|
|
291
292
|
}
|
|
293
|
+
if (["decision", "gotcha", "convention", "architecture"].includes(fm.type) && fm.status !== "rejected" && naked.length >= 40 && specificityScore(naked) < 0.2) {
|
|
294
|
+
out.push({
|
|
295
|
+
file: filePath,
|
|
296
|
+
id: fm.id,
|
|
297
|
+
severity: "info",
|
|
298
|
+
code: "LOW_VALUE_GUESSABLE",
|
|
299
|
+
message: "Reads like generic best practice a capable model already follows. hAIve's value is UNGUESSABLE team knowledge \u2014 add the concrete, arbitrary specifics (exact names, values, formats, magic numbers) or consider removing it to keep briefings high-signal."
|
|
300
|
+
});
|
|
301
|
+
}
|
|
292
302
|
const suggestedAnchors = suggestAnchors(root, { filePath, memory: memory2 }, codeMap, trackedFiles);
|
|
293
303
|
if (ANCHOR_TYPES.has(fm.type) && fm.anchor.paths.length === 0 && fm.status === "validated") {
|
|
294
304
|
out.push({
|
|
@@ -935,12 +945,10 @@ function registerBriefing(program2) {
|
|
|
935
945
|
if (existsSync3(paths.projectContext) && !stopped()) {
|
|
936
946
|
const ctx = await readFile2(paths.projectContext, "utf8");
|
|
937
947
|
const isTemplate = ctx.includes("TODO \u2014 high-level overview") || ctx.includes("Generated by `haive init`");
|
|
938
|
-
|
|
948
|
+
const bootstrapUnfilled = /Auto-generated by `haive init/i.test(ctx) && (ctx.match(/TODO —/g)?.length ?? 0) >= 2;
|
|
949
|
+
if (isTemplate || bootstrapUnfilled) {
|
|
939
950
|
ui.warn(
|
|
940
|
-
"project-context.md still
|
|
941
|
-
);
|
|
942
|
-
ui.warn(
|
|
943
|
-
"Fix: in your AI client, invoke the MCP prompt bootstrap_project to auto-fill it from your codebase."
|
|
951
|
+
"project-context.md is still auto-generated/unfilled \u2014 skipping it (low value). Fill it in, or invoke the bootstrap_project MCP prompt for real context."
|
|
944
952
|
);
|
|
945
953
|
out("");
|
|
946
954
|
} else {
|
|
@@ -3701,6 +3709,7 @@ import { existsSync as existsSync15 } from "fs";
|
|
|
3701
3709
|
import path62 from "path";
|
|
3702
3710
|
import {
|
|
3703
3711
|
buildFrontmatter as buildFrontmatter3,
|
|
3712
|
+
isLikelyGuessable,
|
|
3704
3713
|
memoryFilePath as memoryFilePath3,
|
|
3705
3714
|
serializeMemory as serializeMemory7
|
|
3706
3715
|
} from "@hiveai/core";
|
|
@@ -3750,6 +3759,8 @@ import {
|
|
|
3750
3759
|
queryCodeMap as queryCodeMap2,
|
|
3751
3760
|
resolveBriefingBudget as resolveBriefingBudget2,
|
|
3752
3761
|
serializeMemory as serializeMemory9,
|
|
3762
|
+
specificityScore as specificityScore2,
|
|
3763
|
+
GUESSABLE_THRESHOLD,
|
|
3753
3764
|
tokenizeQuery as tokenizeQuery22,
|
|
3754
3765
|
trackReads as trackReads3,
|
|
3755
3766
|
truncateToTokens,
|
|
@@ -4780,12 +4791,25 @@ var MemObserveInputSchema = {
|
|
|
4780
4791
|
scope: z15.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
|
|
4781
4792
|
module: z15.string().optional().describe("Module name (required when scope=module)"),
|
|
4782
4793
|
tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
|
|
4783
|
-
author: z15.string().optional().describe("Author handle or email")
|
|
4794
|
+
author: z15.string().optional().describe("Author handle or email"),
|
|
4795
|
+
force: z15.boolean().default(false).describe(
|
|
4796
|
+
"Save even if the observation looks like generic, guessable knowledge. By default, low-specificity observations (things a capable model already knows) are SKIPPED to keep the corpus high-signal \u2014 only unguessable, team-specific discoveries are worth storing."
|
|
4797
|
+
)
|
|
4784
4798
|
};
|
|
4785
4799
|
async function memObserve(input, ctx) {
|
|
4786
4800
|
if (!existsSync15(ctx.paths.haiveDir)) {
|
|
4787
4801
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
4788
4802
|
}
|
|
4803
|
+
const signalText = [input.what, input.impact, input.fix ?? ""].join(" ");
|
|
4804
|
+
if (!input.force && isLikelyGuessable(signalText)) {
|
|
4805
|
+
return {
|
|
4806
|
+
id: "",
|
|
4807
|
+
scope: input.scope,
|
|
4808
|
+
file_path: "",
|
|
4809
|
+
skipped: true,
|
|
4810
|
+
reason: "Observation looks like generic, guessable knowledge (low specificity) \u2014 not saved. Capture only arbitrary, team-specific facts (exact names, values, formats). Pass force=true to override."
|
|
4811
|
+
};
|
|
4812
|
+
}
|
|
4789
4813
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 6).join("-");
|
|
4790
4814
|
const anchorPaths = input.where.split(/[,\n]/).map((s) => s.trim()).filter(Boolean);
|
|
4791
4815
|
const baseFm = buildFrontmatter3({
|
|
@@ -5472,6 +5496,14 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5472
5496
|
const memoriesEmpty = outputMemories.length === 0;
|
|
5473
5497
|
const hasMemoriesDir = existsSync18(ctx.paths.memoriesDir);
|
|
5474
5498
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
5499
|
+
const hasUnguessableSignal = outputMemories.some(
|
|
5500
|
+
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore2(m.body) >= GUESSABLE_THRESHOLD
|
|
5501
|
+
);
|
|
5502
|
+
const briefingValueLow = !hasUnguessableSignal;
|
|
5503
|
+
const adaptiveConfig = await loadConfig3(ctx.paths);
|
|
5504
|
+
const bootstrapUnfilled = /Auto-generated by `haive init/i.test(projectContextRaw) && (projectContextRaw.match(/TODO —/g)?.length ?? 0) >= 2;
|
|
5505
|
+
const contextIsInferable = isTemplateContext || autoContextGenerated || bootstrapUnfilled;
|
|
5506
|
+
const adaptiveTrim = adaptiveConfig.adaptiveBriefing !== false && briefingValueLow && contextIsInferable;
|
|
5475
5507
|
const hints = [];
|
|
5476
5508
|
if (isColdStart) {
|
|
5477
5509
|
hints.push(
|
|
@@ -5504,6 +5536,11 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5504
5536
|
);
|
|
5505
5537
|
}
|
|
5506
5538
|
}
|
|
5539
|
+
if (adaptiveTrim) {
|
|
5540
|
+
hints.push(
|
|
5541
|
+
"No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
|
|
5542
|
+
);
|
|
5543
|
+
}
|
|
5507
5544
|
if (existsSync18(ctx.paths.haiveDir)) {
|
|
5508
5545
|
await writeBriefingMarker2(ctx.paths, {
|
|
5509
5546
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
@@ -5519,7 +5556,12 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5519
5556
|
search_mode: searchMode,
|
|
5520
5557
|
inferred_modules: inferred,
|
|
5521
5558
|
...lastSession ? { last_session: lastSession } : {},
|
|
5522
|
-
project_context:
|
|
5559
|
+
project_context: adaptiveTrim ? {
|
|
5560
|
+
content: "(adaptive briefing: auto-generated context omitted \u2014 no team-specific policy matched, so a capable model needs nothing extra here)",
|
|
5561
|
+
truncated: false,
|
|
5562
|
+
...isTemplateContext && !autoContextGenerated ? { is_template: true } : {},
|
|
5563
|
+
...autoContextGenerated ? { auto_generated: true } : {}
|
|
5564
|
+
} : projectContextRaw || autoContextGenerated ? {
|
|
5523
5565
|
content: projectSlice.text,
|
|
5524
5566
|
truncated: projectSlice.truncated,
|
|
5525
5567
|
...isTemplateContext && !autoContextGenerated ? { is_template: true } : {},
|
|
@@ -5533,6 +5575,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
5533
5575
|
decay_warnings: decayWarnings,
|
|
5534
5576
|
setup_warnings: setupWarnings,
|
|
5535
5577
|
...isColdStart ? { low_value: true } : {},
|
|
5578
|
+
briefing_value: briefingValueLow ? "low" : "high",
|
|
5536
5579
|
...hints.length > 0 ? { hints } : {},
|
|
5537
5580
|
estimated_tokens: totalTokens,
|
|
5538
5581
|
budget: {
|
|
@@ -6061,8 +6104,12 @@ var CODE_STOPWORDS = /* @__PURE__ */ new Set([
|
|
|
6061
6104
|
"console"
|
|
6062
6105
|
]);
|
|
6063
6106
|
function tokenizeDiffForLiteral(diff) {
|
|
6064
|
-
const
|
|
6065
|
-
const
|
|
6107
|
+
const lines = diff.split("\n");
|
|
6108
|
+
const looksLikeDiff = lines.some((l) => /^[+-]/.test(l));
|
|
6109
|
+
const addedOnly = looksLikeDiff ? lines.filter((l) => l.startsWith("+") && !l.startsWith("+++")).join("\n") : diff;
|
|
6110
|
+
const source = addedOnly.trim().length > 0 ? addedOnly : diff;
|
|
6111
|
+
const wsTokens = tokenizeQuery3(source);
|
|
6112
|
+
const wordTokens = source.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 4 && !CODE_STOPWORDS.has(t));
|
|
6066
6113
|
return [.../* @__PURE__ */ new Set([...wsTokens, ...wordTokens])];
|
|
6067
6114
|
}
|
|
6068
6115
|
async function antiPatternsCheck(input, ctx) {
|
|
@@ -7323,7 +7370,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7323
7370
|
};
|
|
7324
7371
|
}
|
|
7325
7372
|
var SERVER_NAME = "haive";
|
|
7326
|
-
var SERVER_VERSION = "0.
|
|
7373
|
+
var SERVER_VERSION = "0.10.0";
|
|
7327
7374
|
function jsonResult(data) {
|
|
7328
7375
|
return {
|
|
7329
7376
|
content: [
|
|
@@ -12043,6 +12090,7 @@ import "commander";
|
|
|
12043
12090
|
import {
|
|
12044
12091
|
findProjectRoot as findProjectRoot41,
|
|
12045
12092
|
getUsage as getUsage18,
|
|
12093
|
+
loadConfig as loadConfig10,
|
|
12046
12094
|
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
12047
12095
|
loadUsageIndex as loadUsageIndex24,
|
|
12048
12096
|
resolveHaivePaths as resolveHaivePaths37,
|
|
@@ -12052,7 +12100,7 @@ var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
|
|
|
12052
12100
|
function registerMemoryArchive(memory2) {
|
|
12053
12101
|
memory2.command("archive").description(
|
|
12054
12102
|
"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."
|
|
12055
|
-
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m')
|
|
12103
|
+
).option("--since <window>", "minimum age since last read (e.g. '180d', '6m'). Default: enforcement.decayAfterDays or 180d").option("--type <type>", "limit to a memory type (default 'attempt'). Pass 'all' to scan all types.", "attempt").option("--unread", "decay by unread-age ALONE (ignore anchor status) \u2014 more aggressive corpus hygiene", false).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) => {
|
|
12056
12104
|
const root = findProjectRoot41(opts.dir);
|
|
12057
12105
|
const paths = resolveHaivePaths37(root);
|
|
12058
12106
|
if (!existsSync60(paths.memoriesDir)) {
|
|
@@ -12060,7 +12108,9 @@ function registerMemoryArchive(memory2) {
|
|
|
12060
12108
|
process.exitCode = 1;
|
|
12061
12109
|
return;
|
|
12062
12110
|
}
|
|
12063
|
-
const
|
|
12111
|
+
const config = await loadConfig10(paths);
|
|
12112
|
+
const defaultWindow = config.enforcement?.decayAfterDays ? `${config.enforcement.decayAfterDays}d` : "180d";
|
|
12113
|
+
const minDays = parseDays(opts.since ?? defaultWindow);
|
|
12064
12114
|
if (minDays === null) {
|
|
12065
12115
|
ui.error(`Invalid --since value: ${opts.since}. Use formats like '180d', '6m', '1y'.`);
|
|
12066
12116
|
process.exitCode = 1;
|
|
@@ -12074,20 +12124,22 @@ function registerMemoryArchive(memory2) {
|
|
|
12074
12124
|
for (const { memory: mem, filePath } of all) {
|
|
12075
12125
|
const fm = mem.frontmatter;
|
|
12076
12126
|
if (typeFilter && fm.type !== typeFilter) continue;
|
|
12127
|
+
if (fm.type === "session_recap" || fm.requires_human_approval) continue;
|
|
12077
12128
|
if (fm.status === "deprecated" || fm.status === "rejected") continue;
|
|
12078
12129
|
const hasAnyAnchor = fm.anchor.paths.length + fm.anchor.symbols.length > 0;
|
|
12079
12130
|
const allPathsGone = fm.anchor.paths.length > 0 && fm.anchor.paths.every((p) => !existsSync60(path44.join(paths.root, p)));
|
|
12080
12131
|
const isAnchorless = !hasAnyAnchor;
|
|
12081
|
-
if (!isAnchorless && !allPathsGone) continue;
|
|
12132
|
+
if (!opts.unread && !isAnchorless && !allPathsGone) continue;
|
|
12082
12133
|
const u = getUsage18(usage, fm.id);
|
|
12083
12134
|
const lastSeen = u.last_read_at ?? fm.created_at;
|
|
12084
12135
|
if (Date.parse(lastSeen) >= cutoff) continue;
|
|
12136
|
+
const reason = isAnchorless ? `anchorless and not read since ${lastSeen.slice(0, 10)}` : allPathsGone ? `all ${fm.anchor.paths.length} anchored path(s) missing and not read since ${lastSeen.slice(0, 10)}` : `not read since ${lastSeen.slice(0, 10)} (unread decay)`;
|
|
12085
12137
|
candidates.push({
|
|
12086
12138
|
id: fm.id,
|
|
12087
12139
|
type: fm.type,
|
|
12088
12140
|
status: fm.status,
|
|
12089
12141
|
last_seen: lastSeen,
|
|
12090
|
-
reason
|
|
12142
|
+
reason,
|
|
12091
12143
|
filePath
|
|
12092
12144
|
});
|
|
12093
12145
|
}
|
|
@@ -12160,7 +12212,7 @@ import {
|
|
|
12160
12212
|
findProjectRoot as findProjectRoot42,
|
|
12161
12213
|
getUsage as getUsage19,
|
|
12162
12214
|
loadCodeMap as loadCodeMap7,
|
|
12163
|
-
loadConfig as
|
|
12215
|
+
loadConfig as loadConfig11,
|
|
12164
12216
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
12165
12217
|
loadUsageIndex as loadUsageIndex25,
|
|
12166
12218
|
readUsageEvents as readUsageEvents4,
|
|
@@ -12175,7 +12227,7 @@ function registerDoctor(program2) {
|
|
|
12175
12227
|
const paths = resolveHaivePaths38(root);
|
|
12176
12228
|
const findings = [];
|
|
12177
12229
|
const repairs = [];
|
|
12178
|
-
const config = await
|
|
12230
|
+
const config = await loadConfig11(paths);
|
|
12179
12231
|
if (!existsSync61(paths.haiveDir)) {
|
|
12180
12232
|
findings.push({
|
|
12181
12233
|
severity: "error",
|
|
@@ -12395,14 +12447,14 @@ function registerDoctor(program2) {
|
|
|
12395
12447
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
12396
12448
|
});
|
|
12397
12449
|
}
|
|
12398
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
12450
|
+
findings.push(...await collectInstallFindings(root, "0.10.0"));
|
|
12399
12451
|
try {
|
|
12400
12452
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
12401
12453
|
encoding: "utf8",
|
|
12402
12454
|
timeout: 3e3,
|
|
12403
12455
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12404
12456
|
}).trim();
|
|
12405
|
-
const cliVersion = "0.
|
|
12457
|
+
const cliVersion = "0.10.0";
|
|
12406
12458
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
12407
12459
|
findings.push({
|
|
12408
12460
|
severity: "warn",
|
|
@@ -12982,7 +13034,9 @@ function truncate3(text, max) {
|
|
|
12982
13034
|
import { spawn as spawn5 } from "child_process";
|
|
12983
13035
|
import "commander";
|
|
12984
13036
|
import {
|
|
13037
|
+
antiPatternGateParams,
|
|
12985
13038
|
findProjectRoot as findProjectRoot44,
|
|
13039
|
+
loadConfig as loadConfig12,
|
|
12986
13040
|
resolveHaivePaths as resolveHaivePaths40
|
|
12987
13041
|
} from "@hiveai/core";
|
|
12988
13042
|
function registerPrecommit(program2) {
|
|
@@ -12990,8 +13044,7 @@ function registerPrecommit(program2) {
|
|
|
12990
13044
|
"Run a pre-commit safety check: scans `git diff --cached` against known anti-patterns,\n surfaces conventions/decisions anchored to touched files, and warns about stale anchored memories.\n\n Wire it into git as: `.git/hooks/pre-commit` running `haive precommit` (exit 1 = block).\n\n Examples:\n haive precommit # auto-detects staged diff\n haive precommit --block-on any # block on any warning, not just high-confidence\n haive precommit --paths src/auth.ts src/db.ts # explicit paths instead of git diff"
|
|
12991
13045
|
).option(
|
|
12992
13046
|
"--block-on <mode>",
|
|
12993
|
-
"'any' | 'high-confidence'
|
|
12994
|
-
"high-confidence"
|
|
13047
|
+
"'any' | 'high-confidence' | 'never' (report only). Default: derived from enforcement.antiPatternGate."
|
|
12995
13048
|
).option("--no-semantic", "disable semantic search in anti-patterns matching").option(
|
|
12996
13049
|
"--no-anchored-blocks",
|
|
12997
13050
|
"do not block on anchored, diff-corroborated anti-patterns (only block on very strong semantic matches)"
|
|
@@ -12999,6 +13052,11 @@ function registerPrecommit(program2) {
|
|
|
12999
13052
|
const root = findProjectRoot44(opts.dir);
|
|
13000
13053
|
const paths = resolveHaivePaths40(root);
|
|
13001
13054
|
const ctx = { paths };
|
|
13055
|
+
const config = await loadConfig12(paths);
|
|
13056
|
+
const gate = config.enforcement?.antiPatternGate ?? "anchored";
|
|
13057
|
+
const gateParams = antiPatternGateParams(gate);
|
|
13058
|
+
const blockOn = opts.blockOn ?? gateParams.block_on;
|
|
13059
|
+
const anchoredBlocks = opts.anchoredBlocks === false ? false : gateParams.anchored_blocks;
|
|
13002
13060
|
let diff = "";
|
|
13003
13061
|
let touchedPaths = opts.paths ?? [];
|
|
13004
13062
|
if (touchedPaths.length === 0) {
|
|
@@ -13036,8 +13094,8 @@ function registerPrecommit(program2) {
|
|
|
13036
13094
|
const result = await preCommitCheck({
|
|
13037
13095
|
diff: diff || void 0,
|
|
13038
13096
|
paths: touchedPaths,
|
|
13039
|
-
block_on:
|
|
13040
|
-
anchored_blocks:
|
|
13097
|
+
block_on: blockOn,
|
|
13098
|
+
anchored_blocks: anchoredBlocks,
|
|
13041
13099
|
semantic: opts.noSemantic ? false : true
|
|
13042
13100
|
}, ctx);
|
|
13043
13101
|
if (opts.json) {
|
|
@@ -13080,7 +13138,7 @@ function registerPrecommit(program2) {
|
|
|
13080
13138
|
console.log();
|
|
13081
13139
|
}
|
|
13082
13140
|
if (result.should_block) {
|
|
13083
|
-
ui.error(`Blocking commit (block_on=${
|
|
13141
|
+
ui.error(`Blocking commit (gate=${gate}, block_on=${blockOn}). Address the warnings above or pass --block-on never to bypass.`);
|
|
13084
13142
|
process.exit(1);
|
|
13085
13143
|
}
|
|
13086
13144
|
if (result.warnings.length === 0 && result.stale_anchors.length === 0) {
|
|
@@ -13366,10 +13424,11 @@ import { chmod as chmod2, mkdir as mkdir19, readFile as readFile21, readdir as r
|
|
|
13366
13424
|
import path50 from "path";
|
|
13367
13425
|
import "commander";
|
|
13368
13426
|
import {
|
|
13427
|
+
antiPatternGateParams as antiPatternGateParams2,
|
|
13369
13428
|
findProjectRoot as findProjectRoot49,
|
|
13370
13429
|
hasRecentBriefingMarker,
|
|
13371
13430
|
isFreshIsoDate,
|
|
13372
|
-
loadConfig as
|
|
13431
|
+
loadConfig as loadConfig13,
|
|
13373
13432
|
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
13374
13433
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
13375
13434
|
readRecentBriefingMarker,
|
|
@@ -13390,7 +13449,7 @@ function registerEnforce(program2) {
|
|
|
13390
13449
|
const root = findProjectRoot49(opts.dir);
|
|
13391
13450
|
const paths = resolveHaivePaths45(root);
|
|
13392
13451
|
await mkdir19(paths.haiveDir, { recursive: true });
|
|
13393
|
-
const current = await
|
|
13452
|
+
const current = await loadConfig13(paths);
|
|
13394
13453
|
await saveConfig4(paths, {
|
|
13395
13454
|
...current,
|
|
13396
13455
|
enforcement: {
|
|
@@ -13663,7 +13722,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13663
13722
|
const root = findProjectRoot49(dir);
|
|
13664
13723
|
const paths = resolveHaivePaths45(root);
|
|
13665
13724
|
const initialized = existsSync68(paths.haiveDir);
|
|
13666
|
-
const config = initialized ? await
|
|
13725
|
+
const config = initialized ? await loadConfig13(paths) : {};
|
|
13667
13726
|
if (initialized) await applyLightweightRepairs(root, paths);
|
|
13668
13727
|
const mode = config.enforcement?.mode ?? "strict";
|
|
13669
13728
|
const findings = [];
|
|
@@ -13693,7 +13752,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
13693
13752
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
13694
13753
|
});
|
|
13695
13754
|
}
|
|
13696
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
13755
|
+
findings.push(...await inspectIntegrationVersions(root, "0.10.0"));
|
|
13697
13756
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
13698
13757
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
13699
13758
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -13861,11 +13920,12 @@ async function runPrecommitPolicy(paths, gate) {
|
|
|
13861
13920
|
return [{ severity: "info", code: "no-staged-changes", message: "No staged changes found for pre-commit policy." }];
|
|
13862
13921
|
}
|
|
13863
13922
|
const diff = await runCommand4("git", ["diff", "--cached"], paths.root).catch(() => "");
|
|
13923
|
+
const { block_on, anchored_blocks } = antiPatternGateParams2(gate);
|
|
13864
13924
|
const result = await preCommitCheck({
|
|
13865
13925
|
diff,
|
|
13866
13926
|
paths: touchedPaths,
|
|
13867
|
-
block_on
|
|
13868
|
-
anchored_blocks
|
|
13927
|
+
block_on,
|
|
13928
|
+
anchored_blocks,
|
|
13869
13929
|
semantic: true
|
|
13870
13930
|
}, { paths });
|
|
13871
13931
|
if (!result.should_block) {
|
|
@@ -14304,7 +14364,7 @@ function registerRun(program2) {
|
|
|
14304
14364
|
|
|
14305
14365
|
// src/index.ts
|
|
14306
14366
|
var program = new Command52();
|
|
14307
|
-
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.
|
|
14367
|
+
program.name("haive").description("hAIve \u2014 the memory and enforcement layer of your agent harness").version("0.10.0").option("--advanced", "show maintenance and experimental commands in help");
|
|
14308
14368
|
registerInit(program);
|
|
14309
14369
|
registerWelcome(program);
|
|
14310
14370
|
registerResolveProject(program);
|
|
@@ -14363,17 +14423,14 @@ registerPrecommit(program);
|
|
|
14363
14423
|
var CORE_ROOT_COMMANDS = /* @__PURE__ */ new Set([
|
|
14364
14424
|
"init",
|
|
14365
14425
|
"doctor",
|
|
14366
|
-
"tui",
|
|
14367
14426
|
"agent",
|
|
14427
|
+
"briefing",
|
|
14368
14428
|
"enforce",
|
|
14369
14429
|
"run",
|
|
14370
|
-
"briefing",
|
|
14371
14430
|
"sync",
|
|
14372
14431
|
"mcp",
|
|
14373
14432
|
"memory",
|
|
14374
|
-
"session"
|
|
14375
|
-
"precommit",
|
|
14376
|
-
"welcome"
|
|
14433
|
+
"session"
|
|
14377
14434
|
]);
|
|
14378
14435
|
var CORE_MEMORY_COMMANDS = /* @__PURE__ */ new Set([
|
|
14379
14436
|
"add",
|