@hivelore/cli 0.49.0 → 0.52.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/{chunk-VW4U72ET.js → chunk-IICT42WP.js} +21 -8
- package/dist/chunk-IICT42WP.js.map +1 -0
- package/dist/index.js +207 -124
- package/dist/index.js.map +1 -1
- package/dist/{server-ZE5Y3Z4A.js → server-UAD33QQZ.js} +2 -2
- package/package.json +5 -7
- package/dist/chunk-VW4U72ET.js.map +0 -1
- /package/dist/{server-ZE5Y3Z4A.js.map → server-UAD33QQZ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readPresumedCorrectTargets,
|
|
14
14
|
runAstSensorOnContent,
|
|
15
15
|
runHaiveMcpStdio
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IICT42WP.js";
|
|
17
17
|
import {
|
|
18
18
|
registerMemoryPending
|
|
19
19
|
} from "./chunk-OYJKHD22.js";
|
|
@@ -588,7 +588,7 @@ async function applyAutopilotRepairs(root, paths, options = {}) {
|
|
|
588
588
|
if (changed) {
|
|
589
589
|
repairs.push({
|
|
590
590
|
code: "autopilot-config",
|
|
591
|
-
message: "Enabled autopilot defaults in .ai/
|
|
591
|
+
message: "Enabled autopilot defaults in .ai/hivelore.config.json."
|
|
592
592
|
});
|
|
593
593
|
}
|
|
594
594
|
}
|
|
@@ -1483,7 +1483,7 @@ function isCodeMapStale(root, generatedAt, files) {
|
|
|
1483
1483
|
|
|
1484
1484
|
// src/commands/init.ts
|
|
1485
1485
|
import { execFile as execFile2 } from "child_process";
|
|
1486
|
-
import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, writeFile as writeFile7 } from "fs/promises";
|
|
1486
|
+
import { mkdir as mkdir6, readFile as readFile6, readdir as readdir2, rm, writeFile as writeFile7 } from "fs/promises";
|
|
1487
1487
|
import { existsSync as existsSync12 } from "fs";
|
|
1488
1488
|
import path13 from "path";
|
|
1489
1489
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -1491,6 +1491,10 @@ import { promisify as promisify2 } from "util";
|
|
|
1491
1491
|
import "commander";
|
|
1492
1492
|
import {
|
|
1493
1493
|
AUTOPILOT_DEFAULTS as AUTOPILOT_DEFAULTS2,
|
|
1494
|
+
CONFIG_FILE,
|
|
1495
|
+
LEGACY_CONFIG_FILE,
|
|
1496
|
+
loadConfig as loadConfig3,
|
|
1497
|
+
resolveConfigPath,
|
|
1494
1498
|
BRIDGE_TARGETS as BRIDGE_TARGETS2,
|
|
1495
1499
|
buildCodeMap as buildCodeMap3,
|
|
1496
1500
|
buildFrontmatter as buildFrontmatter2,
|
|
@@ -3758,7 +3762,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3758
3762
|
|
|
3759
3763
|
// src/commands/init.ts
|
|
3760
3764
|
var execFileAsync = promisify2(execFile2);
|
|
3761
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3765
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.52.0"}`;
|
|
3762
3766
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3763
3767
|
|
|
3764
3768
|
> Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -3804,7 +3808,7 @@ This repository uses **Hivelore**. Running \`hivelore init\` means the team expe
|
|
|
3804
3808
|
|
|
3805
3809
|
Tell the developer to enable the **hivelore** server (e.g. \`hivelore mcp --stdio\` in client config) and restart the client. Do not silently ignore Hivelore.
|
|
3806
3810
|
`;
|
|
3807
|
-
var CI_WORKFLOW = `name:
|
|
3811
|
+
var CI_WORKFLOW = `name: hivelore-sync
|
|
3808
3812
|
|
|
3809
3813
|
on:
|
|
3810
3814
|
push:
|
|
@@ -3926,7 +3930,7 @@ jobs:
|
|
|
3926
3930
|
run: hivelore eval --trend || true
|
|
3927
3931
|
|
|
3928
3932
|
# On push to main: push shared memories to the hub (if hubPath is configured)
|
|
3929
|
-
# Uncomment and configure hubPath in .ai/
|
|
3933
|
+
# Uncomment and configure hubPath in .ai/hivelore.config.json to enable.
|
|
3930
3934
|
# hub-push:
|
|
3931
3935
|
# if: github.event_name == 'push'
|
|
3932
3936
|
# needs: sync-on-merge
|
|
@@ -3944,7 +3948,7 @@ jobs:
|
|
|
3944
3948
|
# run: npm install -g @hivelore/cli
|
|
3945
3949
|
# - name: push shared memories to hub
|
|
3946
3950
|
# run: hivelore hub push --commit
|
|
3947
|
-
# # Requires hubPath in .ai/
|
|
3951
|
+
# # Requires hubPath in .ai/hivelore.config.json pointing to a cloned hub repo.
|
|
3948
3952
|
`;
|
|
3949
3953
|
function registerInit(program2) {
|
|
3950
3954
|
program2.command("init").description(
|
|
@@ -3954,7 +3958,7 @@ function registerInit(program2) {
|
|
|
3954
3958
|
`which agent bridges to generate: 'auto' (default \u2014 clients detected on this machine/repo, plus AGENTS.md) | 'all' | comma-list.
|
|
3955
3959
|
Available: ${BRIDGE_TARGETS2.join(", ")}. Each carries top memories + block sensors.`,
|
|
3956
3960
|
"auto"
|
|
3957
|
-
).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/
|
|
3961
|
+
).option("--with-ci", "write a GitHub Actions workflow (.github/workflows/hivelore-sync.yml) \u2014 included automatically in autopilot mode").option(
|
|
3958
3962
|
"--manual",
|
|
3959
3963
|
"opt out of autopilot: memories require manual approval, no auto-session recap, no auto-context"
|
|
3960
3964
|
).option(
|
|
@@ -4031,14 +4035,16 @@ function registerInit(program2) {
|
|
|
4031
4035
|
ui.success(`Created ${path13.relative(root, paths.projectContext)}`);
|
|
4032
4036
|
}
|
|
4033
4037
|
}
|
|
4034
|
-
const
|
|
4035
|
-
|
|
4036
|
-
);
|
|
4038
|
+
const resolvedConfig = resolveConfigPath(paths);
|
|
4039
|
+
const configExists = existsSync12(resolvedConfig);
|
|
4037
4040
|
if (!configExists) {
|
|
4038
4041
|
await saveConfig2(paths, autopilot ? AUTOPILOT_DEFAULTS2 : { autopilot: false });
|
|
4039
4042
|
ui.success(
|
|
4040
|
-
`Created .ai
|
|
4043
|
+
`Created .ai/${CONFIG_FILE} (mode: ${autopilot ? "autopilot" : "standard"})`
|
|
4041
4044
|
);
|
|
4045
|
+
} else if (!resolvedConfig.endsWith(CONFIG_FILE)) {
|
|
4046
|
+
await saveConfig2(paths, await loadConfig3(paths));
|
|
4047
|
+
ui.info(`Migrated .ai/${LEGACY_CONFIG_FILE} \u2192 .ai/${CONFIG_FILE}.`);
|
|
4042
4048
|
}
|
|
4043
4049
|
const stacksToSeed = await resolveStacksToSeed(root, wantStack);
|
|
4044
4050
|
if (stacksToSeed.length > 0) {
|
|
@@ -4102,13 +4108,19 @@ function registerInit(program2) {
|
|
|
4102
4108
|
}
|
|
4103
4109
|
const wantCi = opts.withCi || autopilot;
|
|
4104
4110
|
if (wantCi) {
|
|
4105
|
-
const
|
|
4111
|
+
const workflowsDir = path13.join(root, ".github", "workflows");
|
|
4112
|
+
const ciPath = path13.join(workflowsDir, "hivelore-sync.yml");
|
|
4113
|
+
const legacyCiPath = path13.join(workflowsDir, "haive-sync.yml");
|
|
4106
4114
|
if (existsSync12(ciPath)) {
|
|
4107
4115
|
ui.info("CI workflow already exists \u2014 skipped");
|
|
4108
4116
|
} else {
|
|
4109
|
-
await mkdir6(
|
|
4117
|
+
await mkdir6(workflowsDir, { recursive: true });
|
|
4110
4118
|
await writeFile7(ciPath, CI_WORKFLOW, "utf8");
|
|
4111
4119
|
ui.success(`Created ${path13.relative(root, ciPath)}`);
|
|
4120
|
+
if (existsSync12(legacyCiPath)) {
|
|
4121
|
+
await rm(legacyCiPath, { force: true });
|
|
4122
|
+
ui.info(`Migrated ${path13.relative(root, legacyCiPath)} \u2192 ${path13.relative(root, ciPath)}.`);
|
|
4123
|
+
}
|
|
4112
4124
|
}
|
|
4113
4125
|
}
|
|
4114
4126
|
if (autopilot) {
|
|
@@ -4733,7 +4745,7 @@ import {
|
|
|
4733
4745
|
isDecaying,
|
|
4734
4746
|
isStackPackSeed,
|
|
4735
4747
|
loadCodeMap as loadCodeMap5,
|
|
4736
|
-
loadConfig as
|
|
4748
|
+
loadConfig as loadConfig4,
|
|
4737
4749
|
loadMemoriesFromDir as loadMemoriesFromDir7,
|
|
4738
4750
|
loadSensorLedger,
|
|
4739
4751
|
loadUsageIndex as loadUsageIndex3,
|
|
@@ -4771,7 +4783,7 @@ function registerSync(program2) {
|
|
|
4771
4783
|
const log = (msg) => {
|
|
4772
4784
|
if (!opts.quiet) console.log(msg);
|
|
4773
4785
|
};
|
|
4774
|
-
const config = await
|
|
4786
|
+
const config = await loadConfig4(paths);
|
|
4775
4787
|
const autoApproveDelayHours = config.autoApproveDelayHours ?? null;
|
|
4776
4788
|
const autoPromoteMinReads = config.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads;
|
|
4777
4789
|
const autoRepair = config.autoRepair ?? {};
|
|
@@ -5437,7 +5449,7 @@ import {
|
|
|
5437
5449
|
suggestSensorSeed,
|
|
5438
5450
|
findProjectRoot as findProjectRoot10,
|
|
5439
5451
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
5440
|
-
loadConfig as
|
|
5452
|
+
loadConfig as loadConfig5,
|
|
5441
5453
|
loadMemoriesFromDir as loadMemoriesFromDir8,
|
|
5442
5454
|
memoryFilePath as memoryFilePath3,
|
|
5443
5455
|
resolveHaivePaths as resolveHaivePaths9,
|
|
@@ -5477,7 +5489,7 @@ function registerMemoryAdd(memory2) {
|
|
|
5477
5489
|
process.exitCode = 1;
|
|
5478
5490
|
return;
|
|
5479
5491
|
}
|
|
5480
|
-
const config = await
|
|
5492
|
+
const config = await loadConfig5(paths);
|
|
5481
5493
|
const userTags = parseCsv2(opts.tags);
|
|
5482
5494
|
const anchorPaths = parseCsv2(opts.paths ?? opts.files);
|
|
5483
5495
|
const activation = opts.type === "skill" && (opts.activationKeyword || opts.activationGlob || opts.activationAlways) ? {
|
|
@@ -6129,7 +6141,7 @@ import path22 from "path";
|
|
|
6129
6141
|
import "commander";
|
|
6130
6142
|
import {
|
|
6131
6143
|
findProjectRoot as findProjectRoot16,
|
|
6132
|
-
loadConfig as
|
|
6144
|
+
loadConfig as loadConfig6,
|
|
6133
6145
|
resolveHaivePaths as resolveHaivePaths15
|
|
6134
6146
|
} from "@hivelore/core";
|
|
6135
6147
|
async function readDependencyMap(root) {
|
|
@@ -6210,7 +6222,7 @@ function registerMemorySeed(memory2) {
|
|
|
6210
6222
|
ui.success(`Seeded ${total} starter memor${total === 1 ? "y" : "ies"}: ${seededStacks.join(", ")}`);
|
|
6211
6223
|
ui.info("Kept at background priority. Anchor them to a real file (or replace them) to make them high-signal:");
|
|
6212
6224
|
ui.info(" hivelore memory update <id> --paths <key-file> # anchor a seed to a file");
|
|
6213
|
-
const config = await
|
|
6225
|
+
const config = await loadConfig6(paths);
|
|
6214
6226
|
if (config.autopilot || config.autoRepair?.corpus === true) {
|
|
6215
6227
|
const repairs = await applyAutopilotRepairs(root, paths, {
|
|
6216
6228
|
applyConfig: false,
|
|
@@ -6679,7 +6691,7 @@ import {
|
|
|
6679
6691
|
} from "@hivelore/core";
|
|
6680
6692
|
function registerMemoryVerify(memory2) {
|
|
6681
6693
|
memory2.command("verify").description(
|
|
6682
|
-
"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 hivelore sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'hivelore memory verify' to your
|
|
6694
|
+
"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 hivelore sync runs this automatically. Use this command for on-demand checks or in CI.\n\n CI recommendation: add 'hivelore memory verify' to your hivelore-sync.yml PR check job\n to catch stale memories before they reach main.\n\n Examples:\n hivelore memory verify # check all, report only\n hivelore memory verify --update # mark stale/fresh on disk\n hivelore memory verify --id 2026-04-28-gotcha-x # check one memory\n"
|
|
6683
6695
|
).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("--json", "emit machine-readable JSON (for CI / agents)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
6684
6696
|
const root = findProjectRoot24(opts.dir);
|
|
6685
6697
|
const paths = resolveHaivePaths23(root);
|
|
@@ -6966,7 +6978,7 @@ function registerMemoryDigest(program2) {
|
|
|
6966
6978
|
}
|
|
6967
6979
|
|
|
6968
6980
|
// src/commands/session-end.ts
|
|
6969
|
-
import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm } from "fs/promises";
|
|
6981
|
+
import { writeFile as writeFile17, mkdir as mkdir11, readFile as readFile13, rm as rm2 } from "fs/promises";
|
|
6970
6982
|
import { existsSync as existsSync32 } from "fs";
|
|
6971
6983
|
import { spawn as spawn2 } from "child_process";
|
|
6972
6984
|
import path29 from "path";
|
|
@@ -6975,7 +6987,7 @@ import {
|
|
|
6975
6987
|
distillFailureObservations,
|
|
6976
6988
|
buildFrontmatter as buildFrontmatter5,
|
|
6977
6989
|
findProjectRoot as findProjectRoot27,
|
|
6978
|
-
loadConfig as
|
|
6990
|
+
loadConfig as loadConfig7,
|
|
6979
6991
|
loadMemoriesFromDir as loadMemoriesFromDir10,
|
|
6980
6992
|
loadPreventionEvents,
|
|
6981
6993
|
readUsageEvents,
|
|
@@ -7328,10 +7340,10 @@ ${note}` : note;
|
|
|
7328
7340
|
const cleanupObservations = async () => {
|
|
7329
7341
|
if (!opts.auto) return;
|
|
7330
7342
|
const obsFile = path29.join(paths.haiveDir, ".cache", "observations.jsonl");
|
|
7331
|
-
if (existsSync32(obsFile)) await
|
|
7343
|
+
if (existsSync32(obsFile)) await rm2(obsFile).catch(() => {
|
|
7332
7344
|
});
|
|
7333
7345
|
};
|
|
7334
|
-
const config = await
|
|
7346
|
+
const config = await loadConfig7(paths);
|
|
7335
7347
|
if (opts.auto && config.autoSessionRecap === false) {
|
|
7336
7348
|
if (config.sessionHandoff) {
|
|
7337
7349
|
const diffStat = await runGit(root, ["diff", "--stat", "HEAD"]).catch(() => "");
|
|
@@ -7958,7 +7970,7 @@ import {
|
|
|
7958
7970
|
computeGatePrecision,
|
|
7959
7971
|
computeEvalTrend,
|
|
7960
7972
|
findProjectRoot as findProjectRoot31,
|
|
7961
|
-
loadConfig as
|
|
7973
|
+
loadConfig as loadConfig8,
|
|
7962
7974
|
loadEvalHistory,
|
|
7963
7975
|
loadPreventionEvents as loadPreventionEvents3,
|
|
7964
7976
|
loadUsageIndex as loadUsageIndex13,
|
|
@@ -8065,7 +8077,7 @@ function registerEval(program2) {
|
|
|
8065
8077
|
const [usage, preventionEvents, config] = await Promise.all([
|
|
8066
8078
|
loadUsageIndex13(paths),
|
|
8067
8079
|
loadPreventionEvents3(paths),
|
|
8068
|
-
|
|
8080
|
+
loadConfig8(paths)
|
|
8069
8081
|
]);
|
|
8070
8082
|
const gatePrecision = computeGatePrecision(
|
|
8071
8083
|
preventionEvents,
|
|
@@ -8393,7 +8405,7 @@ import {
|
|
|
8393
8405
|
aggregateUsage as aggregateUsage2,
|
|
8394
8406
|
buildFrontmatter as buildFrontmatter6,
|
|
8395
8407
|
findProjectRoot as findProjectRoot32,
|
|
8396
|
-
loadConfig as
|
|
8408
|
+
loadConfig as loadConfig9,
|
|
8397
8409
|
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
8398
8410
|
memoryFilePath as memoryFilePath6,
|
|
8399
8411
|
parseSince as parseSince2,
|
|
@@ -8465,7 +8477,7 @@ function registerMemorySuggest(memory2) {
|
|
|
8465
8477
|
inferred_type: inferType(v.tools, query)
|
|
8466
8478
|
})).sort((a, b) => b.count - a.count);
|
|
8467
8479
|
if (opts.autoSave) {
|
|
8468
|
-
const config = await
|
|
8480
|
+
const config = await loadConfig9(paths);
|
|
8469
8481
|
const topN = Math.max(1, parseInt(opts.topN ?? "3", 10));
|
|
8470
8482
|
const scope = opts.scope === "personal" || opts.scope === "team" ? opts.scope : config.defaultScope ?? "personal";
|
|
8471
8483
|
const status = config.defaultStatus === "validated" ? "validated" : "draft";
|
|
@@ -8684,7 +8696,7 @@ import {
|
|
|
8684
8696
|
findProjectRoot as findProjectRoot34,
|
|
8685
8697
|
getUsage as getUsage8,
|
|
8686
8698
|
retirementSignal as retirementSignal2,
|
|
8687
|
-
loadConfig as
|
|
8699
|
+
loadConfig as loadConfig10,
|
|
8688
8700
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
8689
8701
|
loadUsageIndex as loadUsageIndex14,
|
|
8690
8702
|
resolveHaivePaths as resolveHaivePaths32,
|
|
@@ -8702,7 +8714,7 @@ function registerMemoryArchive(memory2) {
|
|
|
8702
8714
|
process.exitCode = 1;
|
|
8703
8715
|
return;
|
|
8704
8716
|
}
|
|
8705
|
-
const config = await
|
|
8717
|
+
const config = await loadConfig10(paths);
|
|
8706
8718
|
const defaultWindow = config.enforcement?.decayAfterDays ? `${config.enforcement.decayAfterDays}d` : "180d";
|
|
8707
8719
|
const minDays = parseDays(opts.since ?? defaultWindow);
|
|
8708
8720
|
if (minDays === null) {
|
|
@@ -8816,7 +8828,7 @@ import {
|
|
|
8816
8828
|
getUsage as getUsage9,
|
|
8817
8829
|
isStackPackSeed as isStackPackSeed2,
|
|
8818
8830
|
loadCodeMap as loadCodeMap7,
|
|
8819
|
-
loadConfig as
|
|
8831
|
+
loadConfig as loadConfig11,
|
|
8820
8832
|
loadMemoriesFromDirDetailed,
|
|
8821
8833
|
loadSensorLedger as loadSensorLedger2,
|
|
8822
8834
|
loadUsageIndex as loadUsageIndex15,
|
|
@@ -8906,12 +8918,12 @@ var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
|
|
|
8906
8918
|
function registerDoctor(program2) {
|
|
8907
8919
|
program2.command("doctor").description(
|
|
8908
8920
|
"Analyze the local Hivelore 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."
|
|
8909
|
-
).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) => {
|
|
8921
|
+
).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("--all", "show informational findings too (hidden by default to keep doctor actionable)", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
8910
8922
|
const root = findProjectRoot35(opts.dir);
|
|
8911
8923
|
const paths = resolveHaivePaths33(root);
|
|
8912
8924
|
const findings = [];
|
|
8913
8925
|
const repairs = [];
|
|
8914
|
-
const config = await
|
|
8926
|
+
const config = await loadConfig11(paths);
|
|
8915
8927
|
if (!existsSync40(paths.haiveDir)) {
|
|
8916
8928
|
if (opts.json) {
|
|
8917
8929
|
console.log(JSON.stringify({
|
|
@@ -9146,7 +9158,7 @@ function registerDoctor(program2) {
|
|
|
9146
9158
|
}
|
|
9147
9159
|
const astSensorCount = sensorMemories.filter((m) => m.memory.frontmatter.sensor?.kind === "ast").length;
|
|
9148
9160
|
if (astSensorCount > 0) {
|
|
9149
|
-
const { astEngineAvailable: astEngineAvailable2 } = await import("./server-
|
|
9161
|
+
const { astEngineAvailable: astEngineAvailable2 } = await import("./server-UAD33QQZ.js");
|
|
9150
9162
|
if (!await astEngineAvailable2()) {
|
|
9151
9163
|
findings.push({
|
|
9152
9164
|
severity: "warn",
|
|
@@ -9314,11 +9326,11 @@ function registerDoctor(program2) {
|
|
|
9314
9326
|
severity: "info",
|
|
9315
9327
|
code: "no-autopilot",
|
|
9316
9328
|
message: "Autopilot is OFF \u2014 session recaps are not auto-saved on shutdown.",
|
|
9317
|
-
fix: "Edit .ai/
|
|
9329
|
+
fix: "Edit .ai/hivelore.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
|
|
9318
9330
|
});
|
|
9319
9331
|
}
|
|
9320
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
9321
|
-
findings.push(...await collectMcpRuntimeFindings(paths, "0.
|
|
9332
|
+
findings.push(...await collectInstallFindings(root, "0.52.0"));
|
|
9333
|
+
findings.push(...await collectMcpRuntimeFindings(paths, "0.52.0"));
|
|
9322
9334
|
findings.push(...await collectToolchainFindings(root));
|
|
9323
9335
|
try {
|
|
9324
9336
|
const legacyRaw = execSync("haive-mcp --version", {
|
|
@@ -9326,7 +9338,7 @@ function registerDoctor(program2) {
|
|
|
9326
9338
|
timeout: 3e3,
|
|
9327
9339
|
stdio: ["ignore", "pipe", "ignore"]
|
|
9328
9340
|
}).trim();
|
|
9329
|
-
const cliVersion = "0.
|
|
9341
|
+
const cliVersion = "0.52.0";
|
|
9330
9342
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
9331
9343
|
findings.push({
|
|
9332
9344
|
severity: "warn",
|
|
@@ -9453,12 +9465,16 @@ function emit(findings, opts, repairs = []) {
|
|
|
9453
9465
|
"Next actions"
|
|
9454
9466
|
];
|
|
9455
9467
|
const severityOrder = ["error", "warn", "info"];
|
|
9468
|
+
const isVisible = (f) => f.severity !== "info" || opts.all === true || f.alwaysShow === true;
|
|
9469
|
+
let hiddenInfo = 0;
|
|
9456
9470
|
for (const section2 of sectionOrder) {
|
|
9457
9471
|
const sectionFindings = classified.filter((f) => f.section === section2);
|
|
9458
|
-
|
|
9472
|
+
const visible = sectionFindings.filter(isVisible);
|
|
9473
|
+
hiddenInfo += sectionFindings.length - visible.length;
|
|
9474
|
+
if (visible.length === 0) continue;
|
|
9459
9475
|
console.log(ui.bold(section2));
|
|
9460
9476
|
for (const sev of severityOrder) {
|
|
9461
|
-
for (const f of
|
|
9477
|
+
for (const f of visible.filter((x) => x.severity === sev)) {
|
|
9462
9478
|
const icon = sev === "error" ? ui.red("\u2717") : sev === "warn" ? ui.yellow("\u26A0") : ui.dim("\u2139");
|
|
9463
9479
|
console.log(`${icon} ${ui.bold(f.code)} ${f.message}`);
|
|
9464
9480
|
if (opts.fix && f.fix) {
|
|
@@ -9470,6 +9486,9 @@ function emit(findings, opts, repairs = []) {
|
|
|
9470
9486
|
}
|
|
9471
9487
|
console.log();
|
|
9472
9488
|
}
|
|
9489
|
+
if (hiddenInfo > 0) {
|
|
9490
|
+
ui.info(`${hiddenInfo} informational finding(s) hidden \u2014 run \`hivelore doctor --all\` to show them.`);
|
|
9491
|
+
}
|
|
9473
9492
|
if (repairs.length > 0) {
|
|
9474
9493
|
console.log(ui.bold("Autopilot repairs applied"));
|
|
9475
9494
|
for (const repair of repairs) console.log(` ${ui.dim("\u2713")} ${repair.message}`);
|
|
@@ -9576,6 +9595,7 @@ async function collectHarnessCoverageFindings(codeMap, memories) {
|
|
|
9576
9595
|
findings.push({
|
|
9577
9596
|
severity: "info",
|
|
9578
9597
|
code: "harness-coverage",
|
|
9598
|
+
alwaysShow: true,
|
|
9579
9599
|
coverage_percent: pct2,
|
|
9580
9600
|
message: `${covered}/${total} code-map files have validated memory anchors (${pct2}%). ` + coverageDesc + uncoveredHint,
|
|
9581
9601
|
fix: pct2 < 50 && total > 10 ? `hivelore memory save --type gotcha|convention|architecture --paths <key-file> --scope team` : void 0,
|
|
@@ -9587,7 +9607,7 @@ function collectBehaviourCoverageFindings(codeMap, memories) {
|
|
|
9587
9607
|
if (!codeMap) return [];
|
|
9588
9608
|
const codeFiles = Object.keys(codeMap.files);
|
|
9589
9609
|
const cov = assessBehaviourCoverage2({ memories, codeFiles });
|
|
9590
|
-
if (cov.mainAreas.length === 0) return [];
|
|
9610
|
+
if (cov.mainAreas.length === 0 && cov.totalOracles === 0) return [];
|
|
9591
9611
|
const uncoveredHint = cov.uncoveredAreas.length > 0 && cov.totalOracles > 0 ? `
|
|
9592
9612
|
No behavioural oracle: ${cov.uncoveredAreas.slice(0, 5).map((a) => `\`${a}\``).join(", ")}` + (cov.uncoveredAreas.length > 5 ? `, +${cov.uncoveredAreas.length - 5} more` : "") : "";
|
|
9593
9613
|
let fix;
|
|
@@ -9603,8 +9623,9 @@ function collectBehaviourCoverageFindings(codeMap, memories) {
|
|
|
9603
9623
|
return [{
|
|
9604
9624
|
severity: "info",
|
|
9605
9625
|
code: "behaviour-coverage",
|
|
9626
|
+
alwaysShow: true,
|
|
9606
9627
|
section: "Protection",
|
|
9607
|
-
coverage_percent: Math.round(cov.areasWithOracle.length / cov.mainAreas.length * 100),
|
|
9628
|
+
coverage_percent: cov.mainAreas.length > 0 ? Math.round(cov.areasWithOracle.length / cov.mainAreas.length * 100) : cov.totalOracles > 0 ? 100 : 0,
|
|
9608
9629
|
message: `Behaviour harness: ${renderBehaviourCoverageLine2(cov)}.` + uncoveredHint,
|
|
9609
9630
|
fix
|
|
9610
9631
|
}];
|
|
@@ -9955,7 +9976,7 @@ import "commander";
|
|
|
9955
9976
|
import {
|
|
9956
9977
|
antiPatternGateParams,
|
|
9957
9978
|
findProjectRoot as findProjectRoot36,
|
|
9958
|
-
loadConfig as
|
|
9979
|
+
loadConfig as loadConfig12,
|
|
9959
9980
|
resolveHaivePaths as resolveHaivePaths34
|
|
9960
9981
|
} from "@hivelore/core";
|
|
9961
9982
|
function registerPrecommit(program2) {
|
|
@@ -9971,7 +9992,7 @@ function registerPrecommit(program2) {
|
|
|
9971
9992
|
const root = findProjectRoot36(opts.dir);
|
|
9972
9993
|
const paths = resolveHaivePaths34(root);
|
|
9973
9994
|
const ctx = { paths };
|
|
9974
|
-
const config = await
|
|
9995
|
+
const config = await loadConfig12(paths);
|
|
9975
9996
|
const gate = config.enforcement?.antiPatternGate ?? "anchored";
|
|
9976
9997
|
const gateParams = antiPatternGateParams(gate);
|
|
9977
9998
|
const blockOn = opts.blockOn ?? gateParams.block_on;
|
|
@@ -10120,7 +10141,7 @@ function registerResolveProject(program2) {
|
|
|
10120
10141
|
// src/commands/enforce.ts
|
|
10121
10142
|
import { execFile as execFile5, execFileSync as execFileSync2, spawn as spawn4 } from "child_process";
|
|
10122
10143
|
import { existsSync as existsSync42, statSync as statSync4 } from "fs";
|
|
10123
|
-
import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as
|
|
10144
|
+
import { chmod, mkdir as mkdir16, readFile as readFile18, readdir as readdir4, rm as rm3, writeFile as writeFile25 } from "fs/promises";
|
|
10124
10145
|
import path40 from "path";
|
|
10125
10146
|
import { promisify as promisify5 } from "util";
|
|
10126
10147
|
import "commander";
|
|
@@ -10141,7 +10162,7 @@ import {
|
|
|
10141
10162
|
hasRecentBriefingMarker,
|
|
10142
10163
|
isFreshIsoDate,
|
|
10143
10164
|
isRetiredMemory as isRetiredMemory2,
|
|
10144
|
-
loadConfig as
|
|
10165
|
+
loadConfig as loadConfig13,
|
|
10145
10166
|
detectAgentContext,
|
|
10146
10167
|
loadMemoriesFromDir as loadMemoriesFromDir15,
|
|
10147
10168
|
loadSensorLedger as loadSensorLedger3,
|
|
@@ -10439,7 +10460,7 @@ function registerEnforce(program2) {
|
|
|
10439
10460
|
const root = findProjectRoot37(opts.dir);
|
|
10440
10461
|
const paths = resolveHaivePaths35(root);
|
|
10441
10462
|
await mkdir16(paths.haiveDir, { recursive: true });
|
|
10442
|
-
const current = await
|
|
10463
|
+
const current = await loadConfig13(paths);
|
|
10443
10464
|
await saveConfig3(paths, {
|
|
10444
10465
|
...current,
|
|
10445
10466
|
enforcement: {
|
|
@@ -10456,7 +10477,7 @@ function registerEnforce(program2) {
|
|
|
10456
10477
|
policyPacks: ["architecture", "gotchas", "security", "domain", "release"]
|
|
10457
10478
|
}
|
|
10458
10479
|
});
|
|
10459
|
-
ui.success("Hivelore strict enforcement enabled in .ai/
|
|
10480
|
+
ui.success("Hivelore strict enforcement enabled in .ai/hivelore.config.json");
|
|
10460
10481
|
if (opts.git !== false) await installGitEnforcement(root);
|
|
10461
10482
|
if (opts.ci !== false) await installCiEnforcement(root);
|
|
10462
10483
|
if (opts.claude !== false) {
|
|
@@ -10611,7 +10632,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
10611
10632
|
const paths = resolveHaivePaths35(root);
|
|
10612
10633
|
if (!existsSync42(paths.haiveDir)) return;
|
|
10613
10634
|
if (!isWriteLikeTool(payload)) return;
|
|
10614
|
-
const config = await
|
|
10635
|
+
const config = await loadConfig13(paths);
|
|
10615
10636
|
if (config.enforcement?.requireBriefingFirst === false) return;
|
|
10616
10637
|
const gate = config.enforcement?.preEditGate ?? "advise";
|
|
10617
10638
|
const targetFiles = extractToolPaths(payload, root);
|
|
@@ -10625,7 +10646,7 @@ ${briefing.project_context.content.slice(0, 1800)}`);
|
|
|
10625
10646
|
const contextText = buildPreEditContext(payload.tool_name ?? "write tool", targetFiles, missing, hasMarker);
|
|
10626
10647
|
if (gate === "block") {
|
|
10627
10648
|
console.error(
|
|
10628
|
-
contextText + '\n\nThe relevant context is now recorded \u2014 re-issue the same edit to proceed (no `hivelore briefing` command needed). To make this advisory instead of blocking, set `{ "enforcement": { "preEditGate": "advise" } }` in .ai/
|
|
10649
|
+
contextText + '\n\nThe relevant context is now recorded \u2014 re-issue the same edit to proceed (no `hivelore briefing` command needed). To make this advisory instead of blocking, set `{ "enforcement": { "preEditGate": "advise" } }` in .ai/hivelore.config.json.'
|
|
10629
10650
|
);
|
|
10630
10651
|
process.exit(2);
|
|
10631
10652
|
}
|
|
@@ -10691,7 +10712,7 @@ async function buildFinishReport(dir) {
|
|
|
10691
10712
|
const root = findProjectRoot37(dir);
|
|
10692
10713
|
const paths = resolveHaivePaths35(root);
|
|
10693
10714
|
const initialized = existsSync42(paths.haiveDir);
|
|
10694
|
-
const config = initialized ? await
|
|
10715
|
+
const config = initialized ? await loadConfig13(paths) : {};
|
|
10695
10716
|
const mode = config.enforcement?.mode ?? "strict";
|
|
10696
10717
|
const findings = [];
|
|
10697
10718
|
if (!initialized) {
|
|
@@ -10970,7 +10991,7 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
10970
10991
|
HAIVE_ENFORCEMENT: "strict",
|
|
10971
10992
|
HAIVE_AGENT: "1",
|
|
10972
10993
|
// wrapped process is an agent — process gates bind it (detectAgentContext)
|
|
10973
|
-
|
|
10994
|
+
HIVELORE_TOOL_PROFILE: process.env.HIVELORE_TOOL_PROFILE ?? process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
|
|
10974
10995
|
}
|
|
10975
10996
|
});
|
|
10976
10997
|
await new Promise((resolve, reject) => {
|
|
@@ -11100,7 +11121,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
11100
11121
|
const root = findProjectRoot37(dir);
|
|
11101
11122
|
const paths = resolveHaivePaths35(root);
|
|
11102
11123
|
const initialized = existsSync42(paths.haiveDir);
|
|
11103
|
-
const config = initialized ? await
|
|
11124
|
+
const config = initialized ? await loadConfig13(paths) : {};
|
|
11104
11125
|
if (initialized) {
|
|
11105
11126
|
await applyLightweightRepairs(root, paths);
|
|
11106
11127
|
if (stage === "pre-commit") await stageResyncedArtifacts(root, paths);
|
|
@@ -11133,7 +11154,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
11133
11154
|
findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
|
|
11134
11155
|
});
|
|
11135
11156
|
}
|
|
11136
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
11157
|
+
findings.push(...await inspectIntegrationVersions(root, "0.52.0"));
|
|
11137
11158
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
11138
11159
|
const hasBriefing = await hasRecentBriefingMarker(paths, sessionId);
|
|
11139
11160
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
|
|
@@ -11344,7 +11365,7 @@ async function verifyDecisionCoverage(paths, stage, sessionId) {
|
|
|
11344
11365
|
}];
|
|
11345
11366
|
}
|
|
11346
11367
|
if (stage === "pre-commit" || stage === "pre-push") {
|
|
11347
|
-
const cfg = await
|
|
11368
|
+
const cfg = await loadConfig13(paths).catch(() => ({}));
|
|
11348
11369
|
if (cfg.enforcement?.autoBrief !== false) {
|
|
11349
11370
|
await writeBriefingMarker2(paths, {
|
|
11350
11371
|
sessionId,
|
|
@@ -11417,9 +11438,8 @@ async function runPrecommitPolicy(paths, gate, stage, config) {
|
|
|
11417
11438
|
semantic: true
|
|
11418
11439
|
}, { paths });
|
|
11419
11440
|
const sensorFindings = await runSensorGate(paths, snapshot.diff, stage);
|
|
11420
|
-
const
|
|
11421
|
-
|
|
11422
|
-
);
|
|
11441
|
+
const showReview = config.enforcement?.reviewMatches === true || gate === "review";
|
|
11442
|
+
const reviewWarnings = showReview ? result.warnings.filter((w) => w.level === "review" && !w.reasons.includes("sensor")) : [];
|
|
11423
11443
|
const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11424
11444
|
const reviewSeenFile = path40.join(paths.runtimeDir, "enforcement", "review-seen.json");
|
|
11425
11445
|
let reviewSeen = {};
|
|
@@ -11640,7 +11660,7 @@ async function runSensorGate(paths, diff, stage) {
|
|
|
11640
11660
|
}
|
|
11641
11661
|
}
|
|
11642
11662
|
}
|
|
11643
|
-
const config = await
|
|
11663
|
+
const config = await loadConfig13(paths).catch(() => ({}));
|
|
11644
11664
|
if (config?.enforcement?.runCommandSensors === true) {
|
|
11645
11665
|
const changedPaths = targets.map((t) => t.path).filter(Boolean);
|
|
11646
11666
|
const specs = selectCommandSensors(scannable, changedPaths).filter((sp) => !seen.has(sp.memory_id));
|
|
@@ -11725,12 +11745,13 @@ command: ${run.command} (exit ${run.exit_code}, ${run.duration_ms}ms)${outputBlo
|
|
|
11725
11745
|
}
|
|
11726
11746
|
return findings;
|
|
11727
11747
|
} catch (err) {
|
|
11748
|
+
const inCi = stage === "ci";
|
|
11728
11749
|
return [{
|
|
11729
|
-
severity: "warn",
|
|
11750
|
+
severity: inCi ? "error" : "warn",
|
|
11730
11751
|
code: "sensor-gate-errored",
|
|
11731
|
-
message:
|
|
11732
|
-
fix: "Run `hivelore sensors check` to reproduce, and `hivelore doctor` for setup drift.
|
|
11733
|
-
impact:
|
|
11752
|
+
message: `\u26D4 The sensor gate itself errored, so NO sensors were evaluated on this diff \u2014 the deterministic protection was OFF for this run${inCi ? " (failing CI: a passing gate that evaluated nothing is not trustworthy)" : ""}: ` + `${err instanceof Error ? err.message : String(err)}`.slice(0, 500),
|
|
11753
|
+
fix: "Run `hivelore sensors check` to reproduce (set HIVELORE_DEBUG=1 for the stack), and `hivelore doctor` for setup drift. Protection is OFF until this is fixed.",
|
|
11754
|
+
impact: inCi ? 60 : 25
|
|
11734
11755
|
}];
|
|
11735
11756
|
}
|
|
11736
11757
|
}
|
|
@@ -11767,7 +11788,7 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
11767
11788
|
removed += await cleanupEnforcementDir(path40.join(runtimeDir, entry.name));
|
|
11768
11789
|
continue;
|
|
11769
11790
|
}
|
|
11770
|
-
await
|
|
11791
|
+
await rm3(path40.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
11771
11792
|
removed++;
|
|
11772
11793
|
}
|
|
11773
11794
|
await writeFile25(path40.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
@@ -11786,7 +11807,7 @@ async function cleanupCacheDir(cacheDir) {
|
|
|
11786
11807
|
const entries = await readdir4(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
11787
11808
|
for (const entry of entries) {
|
|
11788
11809
|
if (entry.name === ".gitignore") continue;
|
|
11789
|
-
await
|
|
11810
|
+
await rm3(path40.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
11790
11811
|
removed++;
|
|
11791
11812
|
}
|
|
11792
11813
|
await writeFile25(path40.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
@@ -11797,7 +11818,7 @@ async function cleanupEnforcementDir(enforcementDir) {
|
|
|
11797
11818
|
const entries = await readdir4(enforcementDir, { withFileTypes: true }).catch(() => []);
|
|
11798
11819
|
for (const entry of entries) {
|
|
11799
11820
|
if (entry.name === "briefings") continue;
|
|
11800
|
-
await
|
|
11821
|
+
await rm3(path40.join(enforcementDir, entry.name), { recursive: true, force: true });
|
|
11801
11822
|
removed++;
|
|
11802
11823
|
}
|
|
11803
11824
|
return removed;
|
|
@@ -11914,8 +11935,8 @@ async function getPolicyDiffSnapshot(root, stage) {
|
|
|
11914
11935
|
return { diff: "", paths: [], source: "none" };
|
|
11915
11936
|
}
|
|
11916
11937
|
async function resolveCiDiffRange(root) {
|
|
11917
|
-
const explicitBase = cleanGitSha(process.env.HAIVE_BASE_SHA ?? process.env.HAIVE_BASE_REF);
|
|
11918
|
-
const explicitHead = cleanGitSha(process.env.HAIVE_HEAD_SHA ?? process.env.GITHUB_SHA) ?? "HEAD";
|
|
11938
|
+
const explicitBase = cleanGitSha(process.env.HIVELORE_BASE_SHA ?? process.env.HIVELORE_BASE_REF ?? process.env.HAIVE_BASE_SHA ?? process.env.HAIVE_BASE_REF);
|
|
11939
|
+
const explicitHead = cleanGitSha(process.env.HIVELORE_HEAD_SHA ?? process.env.HAIVE_HEAD_SHA ?? process.env.GITHUB_SHA) ?? "HEAD";
|
|
11919
11940
|
if (explicitBase && await gitCommitExists(root, explicitBase)) {
|
|
11920
11941
|
return `${explicitBase}...${explicitHead}`;
|
|
11921
11942
|
}
|
|
@@ -12263,7 +12284,6 @@ async function installGitEnforcement(root) {
|
|
|
12263
12284
|
await mkdir16(hooksDir, { recursive: true });
|
|
12264
12285
|
const resolveCli = `_hivelore() {
|
|
12265
12286
|
if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
|
|
12266
|
-
elif command -v haive >/dev/null 2>&1; then haive "$@"
|
|
12267
12287
|
else return 0
|
|
12268
12288
|
fi
|
|
12269
12289
|
}`;
|
|
@@ -12330,20 +12350,30 @@ ${hook.body}`, "utf8");
|
|
|
12330
12350
|
ui.success("Installed git hooks: pre-commit, pre-push, commit-msg (blocking) + post-merge, post-rewrite (sync)");
|
|
12331
12351
|
}
|
|
12332
12352
|
async function installCiEnforcement(root) {
|
|
12333
|
-
const
|
|
12334
|
-
|
|
12353
|
+
const workflowsDir = path40.join(root, ".github", "workflows");
|
|
12354
|
+
const workflowPath = path40.join(workflowsDir, "hivelore-enforcement.yml");
|
|
12355
|
+
const legacyPath = path40.join(workflowsDir, "haive-enforcement.yml");
|
|
12356
|
+
await mkdir16(workflowsDir, { recursive: true });
|
|
12335
12357
|
const workflow = renderCiEnforcementWorkflow();
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
const
|
|
12339
|
-
const
|
|
12340
|
-
const
|
|
12341
|
-
const endAt = existing.indexOf(
|
|
12358
|
+
const source = existsSync42(workflowPath) ? workflowPath : existsSync42(legacyPath) ? legacyPath : null;
|
|
12359
|
+
if (source) {
|
|
12360
|
+
const existing = await readFile18(source, "utf8");
|
|
12361
|
+
const startAt = Math.max(existing.indexOf("# hivelore:enforcement-workflow:start"), existing.indexOf("# haive:enforcement-workflow:start"));
|
|
12362
|
+
const endMarker = existing.includes("# hivelore:enforcement-workflow:end") ? "# hivelore:enforcement-workflow:end" : "# haive:enforcement-workflow:end";
|
|
12363
|
+
const endAt = existing.indexOf(endMarker);
|
|
12342
12364
|
if (startAt >= 0 && endAt > startAt) {
|
|
12343
|
-
await writeFile25(workflowPath, existing.slice(0, startAt) + workflow + existing.slice(endAt +
|
|
12365
|
+
await writeFile25(workflowPath, existing.slice(0, startAt) + workflow + existing.slice(endAt + endMarker.length), "utf8");
|
|
12344
12366
|
ui.success(`Updated ${path40.relative(root, workflowPath)} managed block`);
|
|
12345
|
-
} else {
|
|
12367
|
+
} else if (source === workflowPath) {
|
|
12346
12368
|
ui.info("GitHub Actions enforcement workflow already exists without Hivelore markers \u2014 preserved");
|
|
12369
|
+
return;
|
|
12370
|
+
} else {
|
|
12371
|
+
await writeFile25(workflowPath, workflow, "utf8");
|
|
12372
|
+
ui.success(`Created ${path40.relative(root, workflowPath)}`);
|
|
12373
|
+
}
|
|
12374
|
+
if (source === legacyPath) {
|
|
12375
|
+
await rm3(legacyPath, { force: true });
|
|
12376
|
+
ui.info(`Migrated ${path40.relative(root, legacyPath)} \u2192 ${path40.relative(root, workflowPath)}.`);
|
|
12347
12377
|
}
|
|
12348
12378
|
return;
|
|
12349
12379
|
}
|
|
@@ -12351,8 +12381,8 @@ async function installCiEnforcement(root) {
|
|
|
12351
12381
|
ui.success(`Created ${path40.relative(root, workflowPath)}`);
|
|
12352
12382
|
}
|
|
12353
12383
|
function renderCiEnforcementWorkflow() {
|
|
12354
|
-
return `#
|
|
12355
|
-
name:
|
|
12384
|
+
return `# hivelore:enforcement-workflow:start
|
|
12385
|
+
name: hivelore-enforcement
|
|
12356
12386
|
|
|
12357
12387
|
on:
|
|
12358
12388
|
pull_request:
|
|
@@ -12360,7 +12390,7 @@ on:
|
|
|
12360
12390
|
branches: [main, master]
|
|
12361
12391
|
|
|
12362
12392
|
jobs:
|
|
12363
|
-
|
|
12393
|
+
hivelore-enforcement:
|
|
12364
12394
|
runs-on: ubuntu-latest
|
|
12365
12395
|
permissions:
|
|
12366
12396
|
contents: read
|
|
@@ -12377,11 +12407,11 @@ jobs:
|
|
|
12377
12407
|
- name: Enforce Hivelore policy
|
|
12378
12408
|
id: gate
|
|
12379
12409
|
env:
|
|
12380
|
-
|
|
12381
|
-
|
|
12410
|
+
HIVELORE_BASE_SHA: \${{ github.event.pull_request.base.sha || github.event.before }}
|
|
12411
|
+
HIVELORE_HEAD_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
12382
12412
|
run: |
|
|
12383
12413
|
set +e
|
|
12384
|
-
hivelore enforce ci --json > "$RUNNER_TEMP/
|
|
12414
|
+
hivelore enforce ci --json > "$RUNNER_TEMP/hivelore-gate.json"
|
|
12385
12415
|
echo "exit_code=$?" >> "$GITHUB_OUTPUT"
|
|
12386
12416
|
exit 0
|
|
12387
12417
|
- name: Upsert prevention receipt
|
|
@@ -12392,7 +12422,7 @@ jobs:
|
|
|
12392
12422
|
run: |
|
|
12393
12423
|
if [ -z "\${GH_TOKEN:-}" ] || ! command -v gh >/dev/null 2>&1; then exit 0; fi
|
|
12394
12424
|
receipt="$(hivelore stats receipt --since 7d --json 2>/dev/null)" || exit 0
|
|
12395
|
-
gate="$(cat "$RUNNER_TEMP/
|
|
12425
|
+
gate="$(cat "$RUNNER_TEMP/hivelore-gate.json" 2>/dev/null)" || gate='{"findings":[]}'
|
|
12396
12426
|
body="$(jq -nr --arg marker '<!-- haive:prevention-receipt -->' --argjson receipt "$receipt" --argjson gate "$gate" '
|
|
12397
12427
|
$marker + "
|
|
12398
12428
|
## Hivelore prevention receipt
|
|
@@ -12421,7 +12451,7 @@ Weekly total: **" + ($receipt.total|tostring) + "** refused; previous window: **
|
|
|
12421
12451
|
- name: Fail when enforcement blocked
|
|
12422
12452
|
if: steps.gate.outputs.exit_code != '0'
|
|
12423
12453
|
run: exit \${{ steps.gate.outputs.exit_code }}
|
|
12424
|
-
#
|
|
12454
|
+
# hivelore:enforcement-workflow:end
|
|
12425
12455
|
`;
|
|
12426
12456
|
}
|
|
12427
12457
|
var CONTENT_CATCH_CODES = /* @__PURE__ */ new Set(["sensor-block", "precommit-policy-block"]);
|
|
@@ -12632,9 +12662,43 @@ import { readFile as readFile19, writeFile as writeFile26 } from "fs/promises";
|
|
|
12632
12662
|
import path41 from "path";
|
|
12633
12663
|
import "commander";
|
|
12634
12664
|
import { findProjectRoot as findProjectRoot38 } from "@hivelore/core";
|
|
12635
|
-
import { execFile as execFile6 } from "child_process";
|
|
12665
|
+
import { execFile as execFile6, spawn as spawn5 } from "child_process";
|
|
12636
12666
|
import { promisify as promisify6 } from "util";
|
|
12637
12667
|
var exec4 = promisify6(execFile6);
|
|
12668
|
+
async function createAndPushTag(root, push) {
|
|
12669
|
+
const version = await readCurrentVersion(root);
|
|
12670
|
+
for (const rel of VERSION_FILES2.slice(1)) {
|
|
12671
|
+
const file = path41.join(root, rel);
|
|
12672
|
+
if (!existsSync43(file)) continue;
|
|
12673
|
+
const v = JSON.parse(await readFile19(file, "utf8")).version;
|
|
12674
|
+
if (v !== version) {
|
|
12675
|
+
ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
|
|
12676
|
+
process.exitCode = 1;
|
|
12677
|
+
return null;
|
|
12678
|
+
}
|
|
12679
|
+
}
|
|
12680
|
+
const dirty = (await exec4("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
|
|
12681
|
+
if (dirty.length > 0) {
|
|
12682
|
+
ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
|
|
12683
|
+
process.exitCode = 1;
|
|
12684
|
+
return null;
|
|
12685
|
+
}
|
|
12686
|
+
const tag = `v${version}`;
|
|
12687
|
+
const existing = (await exec4("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
|
|
12688
|
+
if (existing) {
|
|
12689
|
+
ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
|
|
12690
|
+
process.exitCode = 1;
|
|
12691
|
+
return null;
|
|
12692
|
+
}
|
|
12693
|
+
await exec4("git", ["tag", tag], { cwd: root });
|
|
12694
|
+
ui.success(`Created ${tag} at HEAD.`);
|
|
12695
|
+
if (push) {
|
|
12696
|
+
await exec4("git", ["push"], { cwd: root });
|
|
12697
|
+
await exec4("git", ["push", "origin", tag], { cwd: root });
|
|
12698
|
+
ui.success(`Pushed branch and ${tag}.`);
|
|
12699
|
+
}
|
|
12700
|
+
return tag;
|
|
12701
|
+
}
|
|
12638
12702
|
var VERSION_FILES2 = [
|
|
12639
12703
|
"package.json",
|
|
12640
12704
|
"packages/core/package.json",
|
|
@@ -12700,38 +12764,45 @@ ${heading}
|
|
|
12700
12764
|
});
|
|
12701
12765
|
release.command("tag").description("Create vX.Y.Z at HEAD (from the lockstep version), push the branch and that one tag.").option("--no-push", "create the tag locally without pushing").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12702
12766
|
const root = findProjectRoot38(opts.dir);
|
|
12703
|
-
const
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12767
|
+
const tag = await createAndPushTag(root, opts.push !== false);
|
|
12768
|
+
if (tag && opts.push !== false) {
|
|
12769
|
+
ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
|
|
12770
|
+
}
|
|
12771
|
+
});
|
|
12772
|
+
release.command("ship").description("One-shot release close-out: git pull --rebase \u2192 tag + push \u2192 poll CI (enforce finish --wait). Run it after committing the bump.").option("--no-push", "tag locally without pushing (skips the CI wait)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12773
|
+
const root = findProjectRoot38(opts.dir);
|
|
12774
|
+
const push = opts.push !== false;
|
|
12775
|
+
if (push) {
|
|
12776
|
+
try {
|
|
12777
|
+
await exec4("git", ["pull", "--rebase"], { cwd: root });
|
|
12778
|
+
ui.success("Rebased on origin.");
|
|
12779
|
+
} catch (err) {
|
|
12780
|
+
ui.error(`git pull --rebase failed \u2014 resolve it, then re-run \`hivelore release ship\`:
|
|
12781
|
+
${err instanceof Error ? err.message : String(err)}`);
|
|
12710
12782
|
process.exitCode = 1;
|
|
12711
12783
|
return;
|
|
12712
12784
|
}
|
|
12713
12785
|
}
|
|
12714
|
-
const
|
|
12715
|
-
if (
|
|
12716
|
-
|
|
12717
|
-
|
|
12786
|
+
const tag = await createAndPushTag(root, push);
|
|
12787
|
+
if (!tag) return;
|
|
12788
|
+
if (!push) {
|
|
12789
|
+
ui.info(`Created ${tag} locally (no push). Run \`hivelore release ship\` without --no-push to publish + poll CI.`);
|
|
12718
12790
|
return;
|
|
12719
12791
|
}
|
|
12720
|
-
|
|
12721
|
-
const
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12792
|
+
ui.info("Polling CI (enforce finish --wait)\u2026");
|
|
12793
|
+
const code = await new Promise((resolve) => {
|
|
12794
|
+
const child = spawn5(process.execPath, [process.argv[1], "enforce", "finish", "--wait", "--dir", root], {
|
|
12795
|
+
stdio: "inherit"
|
|
12796
|
+
});
|
|
12797
|
+
child.on("close", (c) => resolve(c ?? 1));
|
|
12798
|
+
child.on("error", () => resolve(1));
|
|
12799
|
+
});
|
|
12800
|
+
if (code !== 0) {
|
|
12801
|
+
ui.error("Shipped the tag, but the finish gate/CI did not pass \u2014 see the output above.");
|
|
12802
|
+
process.exitCode = code;
|
|
12725
12803
|
return;
|
|
12726
12804
|
}
|
|
12727
|
-
|
|
12728
|
-
ui.success(`Created ${tag} at HEAD.`);
|
|
12729
|
-
if (opts.push !== false) {
|
|
12730
|
-
await exec4("git", ["push"], { cwd: root });
|
|
12731
|
-
await exec4("git", ["push", "origin", tag], { cwd: root });
|
|
12732
|
-
ui.success(`Pushed branch and ${tag}.`);
|
|
12733
|
-
ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
|
|
12734
|
-
}
|
|
12805
|
+
ui.success(`Shipped ${tag} \u2014 CI green. npm publication stays a human step (\`pnpm run publish:all\`).`);
|
|
12735
12806
|
});
|
|
12736
12807
|
}
|
|
12737
12808
|
|
|
@@ -12755,6 +12826,7 @@ import path42 from "path";
|
|
|
12755
12826
|
import { promisify as promisify7 } from "util";
|
|
12756
12827
|
import "commander";
|
|
12757
12828
|
import {
|
|
12829
|
+
extractCorrectApproachExamples,
|
|
12758
12830
|
extractSensorExamples,
|
|
12759
12831
|
appendSensorEvaluations as appendSensorEvaluations2,
|
|
12760
12832
|
assessSensorHealth as assessSensorHealth4,
|
|
@@ -12762,7 +12834,7 @@ import {
|
|
|
12762
12834
|
findProjectRoot as findProjectRoot39,
|
|
12763
12835
|
isRetiredMemory as isRetiredMemory3,
|
|
12764
12836
|
judgeProposedSensor,
|
|
12765
|
-
loadConfig as
|
|
12837
|
+
loadConfig as loadConfig14,
|
|
12766
12838
|
loadSensorLedger as loadSensorLedger4,
|
|
12767
12839
|
loadMemoriesFromDir as loadMemoriesFromDir16,
|
|
12768
12840
|
normalizeFramework,
|
|
@@ -12886,7 +12958,7 @@ function registerSensors(program2) {
|
|
|
12886
12958
|
}
|
|
12887
12959
|
}
|
|
12888
12960
|
}
|
|
12889
|
-
const config = await
|
|
12961
|
+
const config = await loadConfig14(paths);
|
|
12890
12962
|
const runCommands = opts.commands || config.enforcement?.runCommandSensors === true;
|
|
12891
12963
|
const changedPaths = targets.map((t) => t.path).filter(Boolean);
|
|
12892
12964
|
const allSensorMemories = await runnableSensorMemories(paths, false);
|
|
@@ -13049,7 +13121,8 @@ function registerSensors(program2) {
|
|
|
13049
13121
|
const currentTargets = await readPresumedCorrectTargets(root, anchorPaths);
|
|
13050
13122
|
const check = sensorSelfCheck2(sensor, {
|
|
13051
13123
|
currentTargets,
|
|
13052
|
-
badExamples: extractSensorExamples(found.memory.body)
|
|
13124
|
+
badExamples: extractSensorExamples(found.memory.body),
|
|
13125
|
+
correctExamples: extractCorrectApproachExamples(found.memory.body)
|
|
13053
13126
|
});
|
|
13054
13127
|
if (currentTargets.length > 0 && !check.silent_on_current) {
|
|
13055
13128
|
ui.error(
|
|
@@ -13058,6 +13131,13 @@ function registerSensors(program2) {
|
|
|
13058
13131
|
process.exitCode = 1;
|
|
13059
13132
|
return;
|
|
13060
13133
|
}
|
|
13134
|
+
if (check.fires_on_correct === true) {
|
|
13135
|
+
ui.error(
|
|
13136
|
+
"Refusing to block: this sensor fires on the lesson's OWN recommended fix (its `Instead, use:` approach) \u2014 it is inverted and would block the correct code, never the mistake. Point the pattern at the FAULTY usage, or pass --force."
|
|
13137
|
+
);
|
|
13138
|
+
process.exitCode = 1;
|
|
13139
|
+
return;
|
|
13140
|
+
}
|
|
13061
13141
|
if (check.fires_on_bad === true) {
|
|
13062
13142
|
ui.success("Self-check passed: fires on the lesson's bad example, silent on current code.");
|
|
13063
13143
|
} else if (check.fires_on_bad === false) {
|
|
@@ -13106,7 +13186,7 @@ function registerSensors(program2) {
|
|
|
13106
13186
|
}
|
|
13107
13187
|
}
|
|
13108
13188
|
const root2 = findProjectRoot39(opts.dir);
|
|
13109
|
-
const { proposeSensor } = await import("./server-
|
|
13189
|
+
const { proposeSensor } = await import("./server-UAD33QQZ.js");
|
|
13110
13190
|
const out = await proposeSensor(
|
|
13111
13191
|
{
|
|
13112
13192
|
memory_id: id,
|
|
@@ -13168,6 +13248,7 @@ function registerSensors(program2) {
|
|
|
13168
13248
|
...opts.badExample ? [opts.badExample] : [],
|
|
13169
13249
|
...extractSensorExamples(found.memory.body)
|
|
13170
13250
|
];
|
|
13251
|
+
const correctExamples = extractCorrectApproachExamples(found.memory.body);
|
|
13171
13252
|
const sensor = {
|
|
13172
13253
|
kind: "regex",
|
|
13173
13254
|
pattern: opts.pattern,
|
|
@@ -13180,7 +13261,7 @@ function registerSensors(program2) {
|
|
|
13180
13261
|
autogen: false,
|
|
13181
13262
|
last_fired: null
|
|
13182
13263
|
};
|
|
13183
|
-
const verdict = judgeProposedSensor(sensor, { currentTargets, badExamples });
|
|
13264
|
+
const verdict = judgeProposedSensor(sensor, { currentTargets, badExamples, correctExamples });
|
|
13184
13265
|
if (opts.json && !verdict.accepted) {
|
|
13185
13266
|
console.log(JSON.stringify({
|
|
13186
13267
|
accepted: verdict.accepted,
|
|
@@ -13197,6 +13278,8 @@ function registerSensors(program2) {
|
|
|
13197
13278
|
ui.error(`Rejected (${verdict.reason}).`);
|
|
13198
13279
|
if (verdict.reason === "fires-on-current") {
|
|
13199
13280
|
ui.warn(`Fires on the CURRENT correct code in: ${verdict.self_check.fired_on.join(", ")}. Add/tighten --absent, then re-run.`);
|
|
13281
|
+
} else if (verdict.reason === "fires-on-correct") {
|
|
13282
|
+
ui.warn("Inverted: the pattern matches the lesson's OWN recommended fix (its `Instead, use:` approach) \u2014 it would block correct code, never the mistake. Point --pattern at the FAULTY usage, then re-run.");
|
|
13200
13283
|
} else if (verdict.reason === "missed-bad-example") {
|
|
13201
13284
|
ui.warn("Did not match the bad example \u2014 the pattern won't catch the mistake. Adjust --pattern, then re-run.");
|
|
13202
13285
|
} else if (verdict.reason === "brittle") {
|
|
@@ -13721,7 +13804,7 @@ import "commander";
|
|
|
13721
13804
|
import {
|
|
13722
13805
|
buildDashboard,
|
|
13723
13806
|
findProjectRoot as findProjectRoot41,
|
|
13724
|
-
loadConfig as
|
|
13807
|
+
loadConfig as loadConfig15,
|
|
13725
13808
|
loadMemoriesFromDir as loadMemoriesFromDir18,
|
|
13726
13809
|
loadPreventionEvents as loadPreventionEvents4,
|
|
13727
13810
|
loadUsageIndex as loadUsageIndex16,
|
|
@@ -13741,7 +13824,7 @@ function registerDashboard(program2) {
|
|
|
13741
13824
|
const memories = existsSync46(paths.memoriesDir) ? await loadMemoriesFromDir18(paths.memoriesDir) : [];
|
|
13742
13825
|
const usage = await loadUsageIndex16(paths);
|
|
13743
13826
|
const preventionEvents = await loadPreventionEvents4(paths);
|
|
13744
|
-
const config = await
|
|
13827
|
+
const config = await loadConfig15(paths);
|
|
13745
13828
|
const top = Math.max(1, Number.parseInt(opts.top ?? "10", 10) || 10);
|
|
13746
13829
|
const dormantDays = opts.dormantDays ? Number.parseInt(opts.dormantDays, 10) : void 0;
|
|
13747
13830
|
const report = buildDashboard(memories, usage, {
|
|
@@ -13872,7 +13955,7 @@ import { findProjectRoot as findProjectRoot42 } from "@hivelore/core";
|
|
|
13872
13955
|
var exec6 = promisify8(execFile8);
|
|
13873
13956
|
function registerDevLink(program2) {
|
|
13874
13957
|
const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
|
|
13875
|
-
dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore
|
|
13958
|
+
dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore install so the global binary runs your local code.").option("-d, --dir <dir>", "repo root (default: discovered from cwd)").option("--json", "emit a machine-readable summary", false).action(async (opts) => {
|
|
13876
13959
|
const root = findProjectRoot42(opts.dir);
|
|
13877
13960
|
if (!existsSync47(path44.join(root, "packages", "cli", "dist", "index.js"))) {
|
|
13878
13961
|
ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
|
|
@@ -13885,9 +13968,9 @@ function registerDevLink(program2) {
|
|
|
13885
13968
|
} catch {
|
|
13886
13969
|
globalModules = path44.join(path44.dirname(path44.dirname(process.execPath)), "lib", "node_modules");
|
|
13887
13970
|
}
|
|
13888
|
-
const scopeDirs = ["@hivelore"
|
|
13971
|
+
const scopeDirs = ["@hivelore"].map((scope) => path44.join(globalModules, scope)).filter((dir) => existsSync47(dir));
|
|
13889
13972
|
if (scopeDirs.length === 0) {
|
|
13890
|
-
ui.error(`No global @hivelore
|
|
13973
|
+
ui.error(`No global @hivelore install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
|
|
13891
13974
|
process.exitCode = 1;
|
|
13892
13975
|
return;
|
|
13893
13976
|
}
|
|
@@ -14173,7 +14256,7 @@ function registerBridges(program2) {
|
|
|
14173
14256
|
|
|
14174
14257
|
// src/index.ts
|
|
14175
14258
|
var program = new Command48();
|
|
14176
|
-
program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.
|
|
14259
|
+
program.name("hivelore").description("Hivelore - the deterministic policy gate for agent-written code (rules live as repo-native team memory)").version("0.52.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
|
|
14177
14260
|
registerInit(program);
|
|
14178
14261
|
registerResolveProject(program);
|
|
14179
14262
|
registerEnforce(program);
|