@hiveai/cli 0.19.0 → 0.20.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 +110 -30
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -262,6 +262,7 @@ import {
|
|
|
262
262
|
loadCodeMap,
|
|
263
263
|
loadMemoriesFromDir,
|
|
264
264
|
loadUsageIndex,
|
|
265
|
+
looksLikeGenericAdvice,
|
|
265
266
|
resolveHaivePaths,
|
|
266
267
|
serializeMemory,
|
|
267
268
|
specificityScore
|
|
@@ -310,7 +311,9 @@ async function lintMemoriesAsync(root, options = {}) {
|
|
|
310
311
|
message: "Record does not contain obvious action/rationale words. Add the concrete rule, why it exists, and what to do instead."
|
|
311
312
|
});
|
|
312
313
|
}
|
|
313
|
-
if (["decision", "gotcha", "convention", "architecture"].includes(fm.type) && fm.status !== "rejected" && naked.length >= 40 && specificityScore(naked) < 0.2
|
|
314
|
+
if (["decision", "gotcha", "convention", "architecture"].includes(fm.type) && fm.status !== "rejected" && naked.length >= 40 && specificityScore(naked) < 0.2 && // High-precision gate: only flag when there is positive evidence of generic advice.
|
|
315
|
+
// A low-density but arbitrary team policy (unguessable prose) must not be flagged.
|
|
316
|
+
looksLikeGenericAdvice(naked)) {
|
|
314
317
|
out.push({
|
|
315
318
|
file: filePath,
|
|
316
319
|
id: fm.id,
|
|
@@ -3309,9 +3312,10 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3309
3312
|
autogen: false,
|
|
3310
3313
|
last_fired: null
|
|
3311
3314
|
} : void 0;
|
|
3315
|
+
const combinedSlug = mem.slug === stack || mem.slug.startsWith(`${stack}-`) ? mem.slug : `${stack}-${mem.slug}`;
|
|
3312
3316
|
const fm = buildFrontmatter({
|
|
3313
3317
|
type: mem.type,
|
|
3314
|
-
slug:
|
|
3318
|
+
slug: combinedSlug,
|
|
3315
3319
|
scope: "team",
|
|
3316
3320
|
status: "validated",
|
|
3317
3321
|
// STACK_PACK_TAG marks this as generic seed knowledge so briefing ranking
|
|
@@ -3334,7 +3338,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3334
3338
|
|
|
3335
3339
|
// src/commands/init.ts
|
|
3336
3340
|
var execFileAsync = promisify2(execFile2);
|
|
3337
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3341
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.20.0"}`;
|
|
3338
3342
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3339
3343
|
|
|
3340
3344
|
> Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -7894,8 +7898,17 @@ function classifyWarning(warning, paths, anchoredBlocks = false) {
|
|
|
7894
7898
|
repair_command: repairCommand
|
|
7895
7899
|
};
|
|
7896
7900
|
}
|
|
7901
|
+
if (warning.has_sensor && !warning.reasons.includes("sensor") && !warning.reasons.includes("anchor")) {
|
|
7902
|
+
return {
|
|
7903
|
+
...warning,
|
|
7904
|
+
level: "info",
|
|
7905
|
+
rationale: "memory has a deterministic sensor that did not fire and is not anchored to a touched file \u2014 treated as non-violation noise",
|
|
7906
|
+
affected_files: affectedFiles,
|
|
7907
|
+
repair_command: repairCommand
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7897
7910
|
const corroborated = warning.reasons.includes("anchor") || warning.distinctive_literal === true;
|
|
7898
|
-
const semanticReviewFloor = corroborated ? 0.45 : 0.
|
|
7911
|
+
const semanticReviewFloor = corroborated ? 0.45 : 0.65;
|
|
7899
7912
|
if (hasSemantic && semanticScore >= semanticReviewFloor || highConfidence && warning.reasons.includes("anchor") && warning.reasons.includes("literal")) {
|
|
7900
7913
|
return {
|
|
7901
7914
|
...warning,
|
|
@@ -8571,7 +8584,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
8571
8584
|
};
|
|
8572
8585
|
}
|
|
8573
8586
|
var SERVER_NAME = "haive";
|
|
8574
|
-
var SERVER_VERSION = "0.
|
|
8587
|
+
var SERVER_VERSION = "0.20.0";
|
|
8575
8588
|
function jsonResult(data) {
|
|
8576
8589
|
return {
|
|
8577
8590
|
content: [
|
|
@@ -14323,7 +14336,7 @@ function registerDoctor(program2) {
|
|
|
14323
14336
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
14324
14337
|
});
|
|
14325
14338
|
}
|
|
14326
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
14339
|
+
findings.push(...await collectInstallFindings(root, "0.20.0"));
|
|
14327
14340
|
findings.push(...await collectToolchainFindings(root));
|
|
14328
14341
|
try {
|
|
14329
14342
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
@@ -14331,7 +14344,7 @@ function registerDoctor(program2) {
|
|
|
14331
14344
|
timeout: 3e3,
|
|
14332
14345
|
stdio: ["ignore", "pipe", "ignore"]
|
|
14333
14346
|
}).trim();
|
|
14334
|
-
const cliVersion = "0.
|
|
14347
|
+
const cliVersion = "0.20.0";
|
|
14335
14348
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
14336
14349
|
findings.push({
|
|
14337
14350
|
severity: "warn",
|
|
@@ -15411,17 +15424,21 @@ import path53 from "path";
|
|
|
15411
15424
|
import "commander";
|
|
15412
15425
|
import {
|
|
15413
15426
|
antiPatternGateParams as antiPatternGateParams2,
|
|
15427
|
+
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH2,
|
|
15414
15428
|
findProjectRoot as findProjectRoot52,
|
|
15415
15429
|
findUncapturedFailures,
|
|
15416
15430
|
hasRecentBriefingMarker as hasRecentBriefingMarker2,
|
|
15417
15431
|
isFreshIsoDate,
|
|
15432
|
+
isRetiredMemory as isRetiredMemory3,
|
|
15418
15433
|
loadConfig as loadConfig14,
|
|
15419
15434
|
loadMemoriesFromDir as loadMemoriesFromDir38,
|
|
15420
15435
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
15421
15436
|
readRecentBriefingMarker,
|
|
15422
15437
|
resolveBriefingBudget as resolveBriefingBudget3,
|
|
15423
15438
|
resolveHaivePaths as resolveHaivePaths48,
|
|
15439
|
+
runSensors as runSensors2,
|
|
15424
15440
|
saveConfig as saveConfig4,
|
|
15441
|
+
sensorTargetsFromDiff as sensorTargetsFromDiff2,
|
|
15425
15442
|
SESSION_RECAP_TTL_MS,
|
|
15426
15443
|
verifyAnchor as verifyAnchor4,
|
|
15427
15444
|
writeBriefingMarker as writeBriefingMarker3
|
|
@@ -16027,7 +16044,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
16027
16044
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
16028
16045
|
});
|
|
16029
16046
|
}
|
|
16030
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
16047
|
+
findings.push(...await inspectIntegrationVersions(root, "0.20.0"));
|
|
16031
16048
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
16032
16049
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
16033
16050
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -16213,20 +16230,83 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
16213
16230
|
anchored_blocks,
|
|
16214
16231
|
semantic: true
|
|
16215
16232
|
}, { paths });
|
|
16233
|
+
const sensorFindings = await runSensorGate(paths, snapshot.diff);
|
|
16216
16234
|
if (!result.should_block) {
|
|
16217
|
-
return [
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16235
|
+
return [
|
|
16236
|
+
{
|
|
16237
|
+
severity: "ok",
|
|
16238
|
+
code: "precommit-policy-pass",
|
|
16239
|
+
message: `${stage === "ci" ? "CI" : "Pre-commit"} policy passed for ${touchedPaths.length} changed file(s).`
|
|
16240
|
+
},
|
|
16241
|
+
...sensorFindings
|
|
16242
|
+
];
|
|
16243
|
+
}
|
|
16244
|
+
return [
|
|
16245
|
+
{
|
|
16246
|
+
severity: "error",
|
|
16247
|
+
code: "precommit-policy-block",
|
|
16248
|
+
message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
|
|
16249
|
+
fix: "Review the hAIve warnings, then update the code or the relevant memories.",
|
|
16250
|
+
impact: 45
|
|
16251
|
+
},
|
|
16252
|
+
...sensorFindings
|
|
16253
|
+
];
|
|
16254
|
+
}
|
|
16255
|
+
var SENSOR_OWNED_FILES = /* @__PURE__ */ new Set([
|
|
16256
|
+
...Object.values(BRIDGE_TARGET_PATH2),
|
|
16257
|
+
"CLAUDE.md",
|
|
16258
|
+
".cursorrules",
|
|
16259
|
+
".gitignore",
|
|
16260
|
+
".mcp.json",
|
|
16261
|
+
".cursor/mcp.json",
|
|
16262
|
+
".vscode/mcp.json",
|
|
16263
|
+
".cursor/rules/haive-mcp-required.mdc"
|
|
16264
|
+
]);
|
|
16265
|
+
function isSensorScannablePath(p) {
|
|
16266
|
+
if (!p) return false;
|
|
16267
|
+
if (p.startsWith(".ai/")) return false;
|
|
16268
|
+
return !SENSOR_OWNED_FILES.has(p);
|
|
16269
|
+
}
|
|
16270
|
+
async function runSensorGate(paths, diff) {
|
|
16271
|
+
if (!diff || !existsSync75(paths.memoriesDir)) return [];
|
|
16272
|
+
try {
|
|
16273
|
+
const loaded = await loadMemoriesFromDir38(paths.memoriesDir);
|
|
16274
|
+
const sensorMemories = loaded.map((l) => l.memory).filter((m) => {
|
|
16275
|
+
const s = m.frontmatter.sensor;
|
|
16276
|
+
return Boolean(s) && s.kind === "regex" && !isRetiredMemory3(m.frontmatter, m.body);
|
|
16277
|
+
});
|
|
16278
|
+
if (sensorMemories.length === 0) return [];
|
|
16279
|
+
const targets = sensorTargetsFromDiff2(diff).filter((t) => isSensorScannablePath(t.path));
|
|
16280
|
+
if (targets.length === 0) return [];
|
|
16281
|
+
const hits = runSensors2(sensorMemories, targets);
|
|
16282
|
+
const findings = [];
|
|
16283
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16284
|
+
for (const hit of hits) {
|
|
16285
|
+
if (seen.has(hit.memory_id)) continue;
|
|
16286
|
+
seen.add(hit.memory_id);
|
|
16287
|
+
const where = hit.file ? ` (${hit.file})` : "";
|
|
16288
|
+
if (hit.severity === "block") {
|
|
16289
|
+
findings.push({
|
|
16290
|
+
severity: "error",
|
|
16291
|
+
code: "sensor-block",
|
|
16292
|
+
message: `Block sensor fired \u2014 ${hit.memory_id}: ${hit.message}${where}`,
|
|
16293
|
+
fix: "Remove the flagged pattern, or run `haive sensors check` to inspect the match.",
|
|
16294
|
+
impact: 45
|
|
16295
|
+
});
|
|
16296
|
+
} else {
|
|
16297
|
+
findings.push({
|
|
16298
|
+
severity: "warn",
|
|
16299
|
+
code: "sensor-warn",
|
|
16300
|
+
message: `Sensor flagged ${hit.memory_id}: ${hit.message}${where}`,
|
|
16301
|
+
fix: "Review the flagged line; `haive sensors check` shows the matched code.",
|
|
16302
|
+
impact: 5
|
|
16303
|
+
});
|
|
16304
|
+
}
|
|
16305
|
+
}
|
|
16306
|
+
return findings;
|
|
16307
|
+
} catch {
|
|
16308
|
+
return [];
|
|
16222
16309
|
}
|
|
16223
|
-
return [{
|
|
16224
|
-
severity: "error",
|
|
16225
|
-
code: "precommit-policy-block",
|
|
16226
|
-
message: `Pre-commit policy matched ${result.summary.blocking_warnings ?? result.summary.anti_patterns} blocking anti-pattern(s), ${result.summary.stale_anchors} stale anchor(s).`,
|
|
16227
|
-
fix: "Review the hAIve warnings, then update the code or the relevant memories.",
|
|
16228
|
-
impact: 45
|
|
16229
|
-
}];
|
|
16230
16310
|
}
|
|
16231
16311
|
async function findGeneratedArtifacts(paths) {
|
|
16232
16312
|
const dirty = await runCommand4("git", ["status", "--short", "--untracked-files=all"], paths.root).catch(() => "");
|
|
@@ -17022,16 +17102,16 @@ import "commander";
|
|
|
17022
17102
|
import {
|
|
17023
17103
|
appendPreventionEvent as appendPreventionEvent2,
|
|
17024
17104
|
findProjectRoot as findProjectRoot53,
|
|
17025
|
-
isRetiredMemory as
|
|
17105
|
+
isRetiredMemory as isRetiredMemory4,
|
|
17026
17106
|
loadConfig as loadConfig15,
|
|
17027
17107
|
loadMemoriesFromDir as loadMemoriesFromDir39,
|
|
17028
17108
|
loadUsageIndex as loadUsageIndex31,
|
|
17029
17109
|
recordPrevention as recordPrevention2,
|
|
17030
17110
|
resolveHaivePaths as resolveHaivePaths49,
|
|
17031
|
-
runSensors as
|
|
17111
|
+
runSensors as runSensors3,
|
|
17032
17112
|
saveUsageIndex as saveUsageIndex8,
|
|
17033
17113
|
selectCommandSensors,
|
|
17034
|
-
sensorTargetsFromDiff as
|
|
17114
|
+
sensorTargetsFromDiff as sensorTargetsFromDiff3,
|
|
17035
17115
|
serializeMemory as serializeMemory29
|
|
17036
17116
|
} from "@hiveai/core";
|
|
17037
17117
|
var exec2 = promisify3(execFile3);
|
|
@@ -17065,8 +17145,8 @@ function registerSensors(program2) {
|
|
|
17065
17145
|
const paths = resolveHaivePaths49(root);
|
|
17066
17146
|
const memories = await runnableSensorMemories(paths);
|
|
17067
17147
|
const diff = opts.diffFile ? await readFile25(path54.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
|
|
17068
|
-
const targets =
|
|
17069
|
-
const hits =
|
|
17148
|
+
const targets = sensorTargetsFromDiff3(diff);
|
|
17149
|
+
const hits = runSensors3(memories, targets.length > 0 ? targets : [{ path: "", content: diff }]);
|
|
17070
17150
|
const config = await loadConfig15(paths);
|
|
17071
17151
|
const runCommands = opts.commands || config.enforcement?.runCommandSensors === true;
|
|
17072
17152
|
const changedPaths = targets.map((t) => t.path).filter(Boolean);
|
|
@@ -17222,7 +17302,7 @@ async function runnableSensorMemories(paths, regexOnly = true) {
|
|
|
17222
17302
|
const sensor = memory2.frontmatter.sensor;
|
|
17223
17303
|
if (!sensor) return false;
|
|
17224
17304
|
if (regexOnly && sensor.kind !== "regex") return false;
|
|
17225
|
-
return !
|
|
17305
|
+
return !isRetiredMemory4(memory2.frontmatter, memory2.body);
|
|
17226
17306
|
});
|
|
17227
17307
|
}
|
|
17228
17308
|
async function runCommandSensor(spec, root) {
|
|
@@ -17924,7 +18004,7 @@ import "commander";
|
|
|
17924
18004
|
import {
|
|
17925
18005
|
findProjectRoot as findProjectRoot61,
|
|
17926
18006
|
resolveHaivePaths as resolveHaivePaths55,
|
|
17927
|
-
BRIDGE_TARGET_PATH as
|
|
18007
|
+
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
|
|
17928
18008
|
BRIDGE_TARGETS as BRIDGE_TARGETS3
|
|
17929
18009
|
} from "@hiveai/core";
|
|
17930
18010
|
function registerBridges(program2) {
|
|
@@ -17959,7 +18039,7 @@ function registerBridges(program2) {
|
|
|
17959
18039
|
targets = BRIDGE_TARGETS3;
|
|
17960
18040
|
} else {
|
|
17961
18041
|
targets = BRIDGE_TARGETS3.filter(
|
|
17962
|
-
(t) => existsSync84(path59.join(root,
|
|
18042
|
+
(t) => existsSync84(path59.join(root, BRIDGE_TARGET_PATH3[t]))
|
|
17963
18043
|
);
|
|
17964
18044
|
if (targets.length === 0) {
|
|
17965
18045
|
ui.info(
|
|
@@ -17987,7 +18067,7 @@ function registerBridges(program2) {
|
|
|
17987
18067
|
const root = findProjectRoot61(opts.dir);
|
|
17988
18068
|
console.log(ui.bold("hAIve bridge targets:"));
|
|
17989
18069
|
for (const target of BRIDGE_TARGETS3) {
|
|
17990
|
-
const relPath =
|
|
18070
|
+
const relPath = BRIDGE_TARGET_PATH3[target];
|
|
17991
18071
|
const exists = existsSync84(path59.join(root, relPath));
|
|
17992
18072
|
const marker = exists ? ui.dim("\u2713") : ui.dim("\xB7");
|
|
17993
18073
|
console.log(` ${marker} ${target.padEnd(10)} ${relPath}${exists ? "" : " (not present)"}`);
|
|
@@ -17999,7 +18079,7 @@ function registerBridges(program2) {
|
|
|
17999
18079
|
|
|
18000
18080
|
// src/index.ts
|
|
18001
18081
|
var program = new Command64();
|
|
18002
|
-
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.
|
|
18082
|
+
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.20.0").option("--advanced", "show maintenance and experimental commands in help");
|
|
18003
18083
|
registerInit(program);
|
|
18004
18084
|
registerWelcome(program);
|
|
18005
18085
|
registerResolveProject(program);
|