@hivelore/cli 0.30.0 → 0.31.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 +618 -362
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command65 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/briefing.ts
|
|
7
7
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
compactAutoRecapBody,
|
|
14
14
|
extractActionsBriefBody,
|
|
15
15
|
findProjectRoot as findProjectRoot2,
|
|
16
|
+
inferModulesFromPaths,
|
|
16
17
|
literalMatchesAllTokens,
|
|
17
18
|
literalMatchesAnyToken,
|
|
18
19
|
loadCodeMap as loadCodeMap3,
|
|
@@ -214,7 +215,7 @@ async function getHotFiles(root, daysBack, maxHotFiles, filePaths) {
|
|
|
214
215
|
if (!f) continue;
|
|
215
216
|
counts.set(f, (counts.get(f) ?? 0) + 1);
|
|
216
217
|
}
|
|
217
|
-
let entries = [...counts.entries()].map(([
|
|
218
|
+
let entries = [...counts.entries()].map(([path64, changes]) => ({ path: path64, changes }));
|
|
218
219
|
const lowerPaths = filePaths.map((p) => p.toLowerCase());
|
|
219
220
|
if (lowerPaths.length > 0) {
|
|
220
221
|
entries = entries.filter((e) => lowerPaths.some((p) => e.path.toLowerCase().includes(p)));
|
|
@@ -757,8 +758,15 @@ async function refreshCodeMap(root, paths, force) {
|
|
|
757
758
|
async function refreshCodeSearchIndex(paths) {
|
|
758
759
|
try {
|
|
759
760
|
const mod = await import("@hivelore/embeddings");
|
|
761
|
+
const cold = !existsSync2(mod.codeIndexPath(paths));
|
|
762
|
+
if (cold) {
|
|
763
|
+
console.error(
|
|
764
|
+
"[hivelore] Building the semantic code index (one-time \u2014 large repos can take a minute). Subsequent briefings reuse the cache."
|
|
765
|
+
);
|
|
766
|
+
}
|
|
760
767
|
const embedder = await mod.Embedder.create();
|
|
761
768
|
const { report } = await mod.rebuildCodeIndex(paths, embedder);
|
|
769
|
+
if (cold) console.error("[hivelore] Semantic code index ready.");
|
|
762
770
|
return report.added > 0 || report.updated > 0 || report.removed > 0;
|
|
763
771
|
} catch {
|
|
764
772
|
return false;
|
|
@@ -1062,11 +1070,21 @@ function registerBriefing(program2) {
|
|
|
1062
1070
|
const usefulCount = priorities.filter((p) => p === "useful").length;
|
|
1063
1071
|
const backgroundCount = priorities.filter((p) => p === "background").length;
|
|
1064
1072
|
const quality = mustReadCount > 0 || usefulCount > 0 ? mustReadCount === 0 && backgroundCount > usefulCount && backgroundCount > 2 ? "noisy" : "strong" : "thin";
|
|
1073
|
+
const inferredModules = inferModulesFromPaths(filePaths);
|
|
1074
|
+
const moduleContexts = [];
|
|
1075
|
+
for (const m of inferredModules) {
|
|
1076
|
+
const ctxFile = path4.join(paths.modulesContextDir, m, "context.md");
|
|
1077
|
+
if (existsSync3(ctxFile)) {
|
|
1078
|
+
moduleContexts.push({ name: m, content: (await readFile2(ctxFile, "utf8")).trim() });
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1065
1081
|
if (json) {
|
|
1066
1082
|
console.log(JSON.stringify({
|
|
1067
1083
|
task: opts.task ?? null,
|
|
1068
1084
|
files: filePaths,
|
|
1069
1085
|
briefing_quality: quality,
|
|
1086
|
+
inferred_modules: inferredModules,
|
|
1087
|
+
module_contexts: moduleContexts,
|
|
1070
1088
|
counts: { must_read: mustReadCount, useful: usefulCount, background: backgroundCount },
|
|
1071
1089
|
recap_id: recaps[0]?.memory.frontmatter.id ?? null,
|
|
1072
1090
|
memories: top.map((item, i) => ({
|
|
@@ -1084,6 +1102,12 @@ function registerBriefing(program2) {
|
|
|
1084
1102
|
}
|
|
1085
1103
|
out(ui.dim(`briefing_quality: ${quality} \xB7 must_read=${mustReadCount} useful=${usefulCount} background=${backgroundCount}`));
|
|
1086
1104
|
out("");
|
|
1105
|
+
for (const mc of moduleContexts) {
|
|
1106
|
+
if (stopped()) break;
|
|
1107
|
+
out(ui.bold(`=== Module context: ${mc.name} ===`));
|
|
1108
|
+
out(mc.content);
|
|
1109
|
+
out("");
|
|
1110
|
+
}
|
|
1087
1111
|
printCliBreadcrumbs({
|
|
1088
1112
|
top,
|
|
1089
1113
|
priorities,
|
|
@@ -3622,12 +3646,18 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3622
3646
|
const DATE_PREFIX = /^\d{4}-\d{2}-\d{2}-/;
|
|
3623
3647
|
const existingTopics = /* @__PURE__ */ new Set();
|
|
3624
3648
|
const existingSignatures = /* @__PURE__ */ new Set();
|
|
3649
|
+
const handWrittenSlugs = [];
|
|
3625
3650
|
if (existsSync10(haivePaths.memoriesDir)) {
|
|
3626
3651
|
for (const { memory: memory2 } of await loadMemoriesFromDir5(haivePaths.memoriesDir)) {
|
|
3627
3652
|
if (memory2.frontmatter.topic) existingTopics.add(memory2.frontmatter.topic);
|
|
3628
3653
|
existingSignatures.add(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
|
|
3654
|
+
if (!memory2.frontmatter.tags.includes("stack-pack")) {
|
|
3655
|
+
handWrittenSlugs.push(memory2.frontmatter.id.replace(DATE_PREFIX, ""));
|
|
3656
|
+
}
|
|
3629
3657
|
}
|
|
3630
3658
|
}
|
|
3659
|
+
const overlapHints = [];
|
|
3660
|
+
const slugTokens = (slug) => new Set(slug.split("-").filter((t) => t.length > 3 && !["convention", "decision", "gotcha", "attempt", "architecture"].includes(t)));
|
|
3631
3661
|
let memCount = 0;
|
|
3632
3662
|
let sensorCount = 0;
|
|
3633
3663
|
for (const mem of memories) {
|
|
@@ -3636,7 +3666,11 @@ async function seedStackPack(haivePaths, stack) {
|
|
|
3636
3666
|
kind: "regex",
|
|
3637
3667
|
pattern: mem.sensor.pattern,
|
|
3638
3668
|
...mem.sensor.flags ? { flags: mem.sensor.flags } : {},
|
|
3639
|
-
|
|
3669
|
+
// Stack rules are stack-wide: when the pack doesn't scope the sensor itself, pin an
|
|
3670
|
+
// explicit repo-wide glob. Leaving [] lets sensorAppliesToPath fall back to the MEMORY's
|
|
3671
|
+
// anchor paths — and seeds get anchored to one exemplar file later, silently shrinking
|
|
3672
|
+
// "never $disconnect() in serverless" to a single file (found in the 0.30.0 field test).
|
|
3673
|
+
paths: mem.sensor.paths ?? ["**"],
|
|
3640
3674
|
message: mem.sensor.message,
|
|
3641
3675
|
severity: "warn",
|
|
3642
3676
|
autogen: false,
|
|
@@ -3674,13 +3708,28 @@ ${SEED_FOOTER(stack)}` });
|
|
|
3674
3708
|
existingSignatures.add(signature);
|
|
3675
3709
|
memCount++;
|
|
3676
3710
|
if (sensor) sensorCount++;
|
|
3711
|
+
const seededTokens = slugTokens(combinedSlug);
|
|
3712
|
+
for (const prior of handWrittenSlugs) {
|
|
3713
|
+
const shared = [...slugTokens(prior)].filter((t) => seededTokens.has(t));
|
|
3714
|
+
if (shared.length >= 2) {
|
|
3715
|
+
overlapHints.push({ seeded: fm.id, existing: prior });
|
|
3716
|
+
break;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
if (overlapHints.length > 0) {
|
|
3721
|
+
ui.warn(`${overlapHints.length} seeded lesson(s) may duplicate existing hand-written memories:`);
|
|
3722
|
+
for (const h of overlapHints.slice(0, 5)) {
|
|
3723
|
+
ui.info(` ${h.seeded} \u2194 ${h.existing}`);
|
|
3724
|
+
}
|
|
3725
|
+
ui.info(" Review with `hivelore memory conflict-candidates`, then `hivelore memory resolve-conflict`.");
|
|
3677
3726
|
}
|
|
3678
3727
|
return { memories: memCount, sensors: sensorCount };
|
|
3679
3728
|
}
|
|
3680
3729
|
|
|
3681
3730
|
// src/commands/init.ts
|
|
3682
3731
|
var execFileAsync = promisify2(execFile2);
|
|
3683
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3732
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.31.0"}`;
|
|
3684
3733
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
3685
3734
|
|
|
3686
3735
|
> Generated by \`hivelore init\`. Run \`hivelore init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -4899,7 +4948,7 @@ import path42 from "path";
|
|
|
4899
4948
|
import {
|
|
4900
4949
|
deriveConfidence as deriveConfidence2,
|
|
4901
4950
|
getUsage as getUsage3,
|
|
4902
|
-
inferModulesFromPaths,
|
|
4951
|
+
inferModulesFromPaths as inferModulesFromPaths2,
|
|
4903
4952
|
loadMemoriesFromDir as loadMemoriesFromDir7,
|
|
4904
4953
|
loadUsageIndex as loadUsageIndex4,
|
|
4905
4954
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
@@ -4940,27 +4989,27 @@ import {
|
|
|
4940
4989
|
serializeMemory as serializeMemory6
|
|
4941
4990
|
} from "@hivelore/core";
|
|
4942
4991
|
import { z as z14 } from "zod";
|
|
4943
|
-
import { mkdir as mkdir32, writeFile as
|
|
4944
|
-
import { existsSync as
|
|
4945
|
-
import
|
|
4992
|
+
import { mkdir as mkdir32, writeFile as writeFile92 } from "fs/promises";
|
|
4993
|
+
import { existsSync as existsSync16 } from "fs";
|
|
4994
|
+
import path62 from "path";
|
|
4946
4995
|
import {
|
|
4947
4996
|
buildFrontmatter as buildFrontmatter22,
|
|
4948
4997
|
memoryFilePath as memoryFilePath22,
|
|
4949
|
-
serializeMemory as
|
|
4998
|
+
serializeMemory as serializeMemory8,
|
|
4950
4999
|
suggestSensorSeed as suggestSensorSeed2
|
|
4951
5000
|
} from "@hivelore/core";
|
|
4952
|
-
import { z as
|
|
5001
|
+
import { z as z16 } from "zod";
|
|
4953
5002
|
import { execSync } from "child_process";
|
|
4954
|
-
import { readFile as readFile32, writeFile as
|
|
4955
|
-
import { existsSync as
|
|
4956
|
-
import
|
|
5003
|
+
import { readFile as readFile32, writeFile as writeFile82 } from "fs/promises";
|
|
5004
|
+
import { existsSync as existsSync152 } from "fs";
|
|
5005
|
+
import path52 from "path";
|
|
4957
5006
|
import {
|
|
4958
5007
|
extractSensorExamples,
|
|
4959
5008
|
judgeProposedSensor,
|
|
4960
5009
|
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
4961
|
-
serializeMemory as
|
|
5010
|
+
serializeMemory as serializeMemory7
|
|
4962
5011
|
} from "@hivelore/core";
|
|
4963
|
-
import { z as
|
|
5012
|
+
import { z as z15 } from "zod";
|
|
4964
5013
|
import { existsSync as existsSync17 } from "fs";
|
|
4965
5014
|
import { mkdir as mkdir42, readFile as readFile42, writeFile as writeFile102 } from "fs/promises";
|
|
4966
5015
|
import path72 from "path";
|
|
@@ -5019,7 +5068,7 @@ import {
|
|
|
5019
5068
|
compactAutoRecapBody as compactAutoRecapBody2,
|
|
5020
5069
|
extractActionsBriefBody as extractActionsBriefBody2,
|
|
5021
5070
|
getUsage as getUsage6,
|
|
5022
|
-
inferModulesFromPaths as
|
|
5071
|
+
inferModulesFromPaths as inferModulesFromPaths22,
|
|
5023
5072
|
isAutoPromoteEligible,
|
|
5024
5073
|
isDecaying,
|
|
5025
5074
|
isRetiredMemory as isRetiredMemory2,
|
|
@@ -5832,7 +5881,7 @@ var MemForFilesInputSchema = {
|
|
|
5832
5881
|
track: z9.boolean().default(true).describe("Increment read_count on returned memories")
|
|
5833
5882
|
};
|
|
5834
5883
|
async function memForFiles(input, ctx) {
|
|
5835
|
-
const inferred =
|
|
5884
|
+
const inferred = inferModulesFromPaths2(input.files);
|
|
5836
5885
|
if (!existsSync92(ctx.paths.memoriesDir)) {
|
|
5837
5886
|
return {
|
|
5838
5887
|
inferred_modules: inferred,
|
|
@@ -6138,71 +6187,17 @@ async function memApprove(input, ctx) {
|
|
|
6138
6187
|
file_path: found.filePath
|
|
6139
6188
|
};
|
|
6140
6189
|
}
|
|
6141
|
-
var MemTriedInputSchema = {
|
|
6142
|
-
what: z15.string().min(1).describe("Brief description of the approach that was tried"),
|
|
6143
|
-
why_failed: z15.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
6144
|
-
instead: z15.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
6145
|
-
scope: z15.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
|
|
6146
|
-
module: z15.string().optional().describe("Module name (required when scope=module)"),
|
|
6147
|
-
tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
|
|
6148
|
-
paths: z15.array(z15.string()).default([]).describe("Anchor file paths this applies to"),
|
|
6149
|
-
author: z15.string().optional().describe("Author handle or email")
|
|
6150
|
-
};
|
|
6151
|
-
async function memTried(input, ctx) {
|
|
6152
|
-
if (!existsSync152(ctx.paths.haiveDir)) {
|
|
6153
|
-
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
6154
|
-
}
|
|
6155
|
-
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
6156
|
-
const baseFm = buildFrontmatter22({
|
|
6157
|
-
type: "attempt",
|
|
6158
|
-
slug,
|
|
6159
|
-
scope: input.scope,
|
|
6160
|
-
module: input.module,
|
|
6161
|
-
tags: input.tags,
|
|
6162
|
-
paths: input.paths,
|
|
6163
|
-
author: input.author
|
|
6164
|
-
});
|
|
6165
|
-
const frontmatter = { ...baseFm, status: "validated" };
|
|
6166
|
-
const lines = [`# ${input.what}`, ""];
|
|
6167
|
-
lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
|
|
6168
|
-
if (input.instead) {
|
|
6169
|
-
lines.push("", `**Instead, use:** ${input.instead}`);
|
|
6170
|
-
}
|
|
6171
|
-
const body = lines.join("\n") + "\n";
|
|
6172
|
-
const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
6173
|
-
await mkdir32(path52.dirname(file), { recursive: true });
|
|
6174
|
-
if (existsSync152(file)) {
|
|
6175
|
-
throw new Error(`Memory already exists at ${file}`);
|
|
6176
|
-
}
|
|
6177
|
-
await writeFile82(file, serializeMemory7({ frontmatter, body }), "utf8");
|
|
6178
|
-
const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
|
|
6179
|
-
const hint = input.paths.length === 0 ? "No `paths` given, so this attempt is feedforward-only \u2014 it will be briefed but the gate cannot block the repeat. Re-run with `paths` set to the file(s) where the mistake lives, then call propose_sensor to close the loop." : seed ? "This attempt is NOT yet enforced. Call propose_sensor to turn it into a reliable block \u2014 a candidate is pre-filled in proposed_sensor_seed (refine it: pattern = the faulty usage, absent = the correct-usage marker). Hivelore validates the proposal (silent on current code, fires on the bad example) before trusting it to block." : "This attempt is NOT yet enforced and no candidate pattern could be derived from the wording. Call propose_sensor with a discriminating pattern (pattern = faulty usage, absent = correct-usage marker) to close the loop.";
|
|
6180
|
-
return {
|
|
6181
|
-
id: frontmatter.id,
|
|
6182
|
-
scope: frontmatter.scope,
|
|
6183
|
-
file_path: file,
|
|
6184
|
-
loop_open: true,
|
|
6185
|
-
...seed ? {
|
|
6186
|
-
proposed_sensor_seed: {
|
|
6187
|
-
pattern: seed.pattern,
|
|
6188
|
-
...seed.absent ? { absent: seed.absent } : {},
|
|
6189
|
-
message: seed.message
|
|
6190
|
-
}
|
|
6191
|
-
} : {},
|
|
6192
|
-
hint
|
|
6193
|
-
};
|
|
6194
|
-
}
|
|
6195
6190
|
var ProposeSensorInputSchema = {
|
|
6196
|
-
memory_id:
|
|
6197
|
-
pattern:
|
|
6198
|
-
absent:
|
|
6191
|
+
memory_id: z15.string().min(1).describe("Id of the gotcha/attempt memory this sensor protects."),
|
|
6192
|
+
pattern: z15.string().min(1).describe("Regex matching the FAULTY usage (the risky call/token), e.g. 'stripe\\.paymentIntents\\.create'."),
|
|
6193
|
+
absent: z15.string().optional().describe(
|
|
6199
6194
|
"Regex for the CORRECT-usage marker (e.g. 'idempotencyKey'). When it appears in the window around a match, the catch is suppressed \u2014 this is what makes the sensor discriminate the faulty call from the correct one. STRONGLY recommended for 'X without Y' lessons."
|
|
6200
6195
|
),
|
|
6201
|
-
bad_example:
|
|
6202
|
-
severity:
|
|
6203
|
-
message:
|
|
6204
|
-
flags:
|
|
6205
|
-
paths:
|
|
6196
|
+
bad_example: z15.string().optional().describe("A code snippet that SHOULD match \u2014 proves the sensor catches the mistake. If omitted, examples are read from the lesson body."),
|
|
6197
|
+
severity: z15.enum(["warn", "block"]).default("block").describe("block = hard-fail the gate (accepted ONLY if it passes self-validation). warn = advisory."),
|
|
6198
|
+
message: z15.string().optional().describe("LLM-facing fix message shown when it fires. Defaults to one derived from the lesson."),
|
|
6199
|
+
flags: z15.string().optional().describe("Optional regex flags (e.g. 'i' for case-insensitive)."),
|
|
6200
|
+
paths: z15.array(z15.string()).default([]).describe("Override scope paths. Defaults to the memory's anchor paths.")
|
|
6206
6201
|
};
|
|
6207
6202
|
function deriveMessage(body, pattern, absent) {
|
|
6208
6203
|
const instead = body.match(/\*\*Instead,\s*use:\*\*\s*([^\n]+)/i)?.[1]?.trim();
|
|
@@ -6227,8 +6222,8 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
6227
6222
|
continue;
|
|
6228
6223
|
} catch {
|
|
6229
6224
|
}
|
|
6230
|
-
const abs =
|
|
6231
|
-
if (!
|
|
6225
|
+
const abs = path52.resolve(root, rel);
|
|
6226
|
+
if (!existsSync152(abs)) continue;
|
|
6232
6227
|
try {
|
|
6233
6228
|
targets.push({ path: rel, content: await readFile32(abs, "utf8") });
|
|
6234
6229
|
} catch {
|
|
@@ -6237,7 +6232,7 @@ async function readPresumedCorrectTargets(root, relPaths) {
|
|
|
6237
6232
|
return targets;
|
|
6238
6233
|
}
|
|
6239
6234
|
async function proposeSensor(input, ctx) {
|
|
6240
|
-
if (!
|
|
6235
|
+
if (!existsSync152(ctx.paths.memoriesDir)) {
|
|
6241
6236
|
throw new Error(`No .ai/memories at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
6242
6237
|
}
|
|
6243
6238
|
try {
|
|
@@ -6297,7 +6292,7 @@ async function proposeSensor(input, ctx) {
|
|
|
6297
6292
|
frontmatter: { ...found.memory.frontmatter, sensor },
|
|
6298
6293
|
body: found.memory.body
|
|
6299
6294
|
};
|
|
6300
|
-
await
|
|
6295
|
+
await writeFile82(found.filePath, serializeMemory7(next), "utf8");
|
|
6301
6296
|
return {
|
|
6302
6297
|
accepted: true,
|
|
6303
6298
|
memory_id: input.memory_id,
|
|
@@ -6306,6 +6301,97 @@ async function proposeSensor(input, ctx) {
|
|
|
6306
6301
|
file_path: found.filePath
|
|
6307
6302
|
};
|
|
6308
6303
|
}
|
|
6304
|
+
var MemTriedInputSchema = {
|
|
6305
|
+
what: z16.string().min(1).describe("Brief description of the approach that was tried"),
|
|
6306
|
+
why_failed: z16.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
6307
|
+
instead: z16.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
6308
|
+
scope: z16.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
|
|
6309
|
+
module: z16.string().optional().describe("Module name (required when scope=module)"),
|
|
6310
|
+
tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
|
|
6311
|
+
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
6312
|
+
author: z16.string().optional().describe("Author handle or email"),
|
|
6313
|
+
sensor: z16.object({
|
|
6314
|
+
pattern: z16.string().min(1).describe("Regex matching the FAULTY usage (added diff lines)"),
|
|
6315
|
+
absent: z16.string().optional().describe("Regex marking CORRECT usage nearby \u2014 excludes it from firing"),
|
|
6316
|
+
severity: z16.enum(["warn", "block"]).default("block").describe("block = deterministic gate refusal"),
|
|
6317
|
+
message: z16.string().optional().describe("Self-correction message shown when the sensor fires"),
|
|
6318
|
+
bad_example: z16.string().optional().describe("Code snippet the sensor MUST fire on (validation)")
|
|
6319
|
+
}).optional().describe(
|
|
6320
|
+
"ONE-SHOT loop close: validate and attach a sensor in the same call (equivalent to a follow-up propose_sensor). Validated against HEAD \u2014 silent on current code, fires on the bad example. If rejected, the attempt is still saved and the verdict tells you how to revise."
|
|
6321
|
+
)
|
|
6322
|
+
};
|
|
6323
|
+
async function memTried(input, ctx) {
|
|
6324
|
+
if (!existsSync16(ctx.paths.haiveDir)) {
|
|
6325
|
+
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
6326
|
+
}
|
|
6327
|
+
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
6328
|
+
const baseFm = buildFrontmatter22({
|
|
6329
|
+
type: "attempt",
|
|
6330
|
+
slug,
|
|
6331
|
+
scope: input.scope,
|
|
6332
|
+
module: input.module,
|
|
6333
|
+
tags: input.tags,
|
|
6334
|
+
paths: input.paths,
|
|
6335
|
+
author: input.author
|
|
6336
|
+
});
|
|
6337
|
+
const frontmatter = { ...baseFm, status: "validated" };
|
|
6338
|
+
const lines = [`# ${input.what}`, ""];
|
|
6339
|
+
lines.push(`**Why it failed / do NOT use:** ${input.why_failed}`);
|
|
6340
|
+
if (input.instead) {
|
|
6341
|
+
lines.push("", `**Instead, use:** ${input.instead}`);
|
|
6342
|
+
}
|
|
6343
|
+
const body = lines.join("\n") + "\n";
|
|
6344
|
+
const file = memoryFilePath22(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
6345
|
+
await mkdir32(path62.dirname(file), { recursive: true });
|
|
6346
|
+
if (existsSync16(file)) {
|
|
6347
|
+
throw new Error(`Memory already exists at ${file}`);
|
|
6348
|
+
}
|
|
6349
|
+
await writeFile92(file, serializeMemory8({ frontmatter, body }), "utf8");
|
|
6350
|
+
if (input.sensor) {
|
|
6351
|
+
const verdict = await proposeSensor(
|
|
6352
|
+
{
|
|
6353
|
+
memory_id: frontmatter.id,
|
|
6354
|
+
pattern: input.sensor.pattern,
|
|
6355
|
+
absent: input.sensor.absent,
|
|
6356
|
+
severity: input.sensor.severity ?? "block",
|
|
6357
|
+
message: input.sensor.message,
|
|
6358
|
+
bad_example: input.sensor.bad_example,
|
|
6359
|
+
flags: void 0,
|
|
6360
|
+
paths: []
|
|
6361
|
+
},
|
|
6362
|
+
ctx
|
|
6363
|
+
);
|
|
6364
|
+
return {
|
|
6365
|
+
id: frontmatter.id,
|
|
6366
|
+
scope: frontmatter.scope,
|
|
6367
|
+
file_path: file,
|
|
6368
|
+
loop_open: !verdict.accepted,
|
|
6369
|
+
sensor_result: {
|
|
6370
|
+
accepted: verdict.accepted,
|
|
6371
|
+
severity: input.sensor.severity ?? "block",
|
|
6372
|
+
...verdict.reason ? { reason: verdict.reason } : {},
|
|
6373
|
+
...verdict.guidance ? { guidance: verdict.guidance } : {}
|
|
6374
|
+
},
|
|
6375
|
+
hint: verdict.accepted ? "Loop closed: the attempt is saved AND enforced \u2014 the gate now refuses a repeat deterministically." : `Attempt saved, but the sensor was rejected (${verdict.reason}). Revise per the guidance and re-propose with propose_sensor.`
|
|
6376
|
+
};
|
|
6377
|
+
}
|
|
6378
|
+
const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
|
|
6379
|
+
const hint = input.paths.length === 0 ? "No `paths` given, so this attempt is feedforward-only \u2014 it will be briefed but the gate cannot block the repeat. Re-run with `paths` set to the file(s) where the mistake lives, then call propose_sensor to close the loop." : seed ? "This attempt is NOT yet enforced. Call propose_sensor (or re-run mem_tried with the one-shot `sensor` parameter) to turn it into a reliable block \u2014 a candidate is pre-filled in proposed_sensor_seed (refine it: pattern = the faulty usage, absent = the correct-usage marker). Hivelore validates the proposal (silent on current code, fires on the bad example) before trusting it to block." : "This attempt is NOT yet enforced and no candidate pattern could be derived from the wording. Call propose_sensor with a discriminating pattern (pattern = faulty usage, absent = correct-usage marker) to close the loop.";
|
|
6380
|
+
return {
|
|
6381
|
+
id: frontmatter.id,
|
|
6382
|
+
scope: frontmatter.scope,
|
|
6383
|
+
file_path: file,
|
|
6384
|
+
loop_open: true,
|
|
6385
|
+
...seed ? {
|
|
6386
|
+
proposed_sensor_seed: {
|
|
6387
|
+
pattern: seed.pattern,
|
|
6388
|
+
...seed.absent ? { absent: seed.absent } : {},
|
|
6389
|
+
message: seed.message
|
|
6390
|
+
}
|
|
6391
|
+
} : {},
|
|
6392
|
+
hint
|
|
6393
|
+
};
|
|
6394
|
+
}
|
|
6309
6395
|
var IngestFindingsInputSchema = {
|
|
6310
6396
|
format: z17.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
|
|
6311
6397
|
report_path: z17.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
|
|
@@ -6886,7 +6972,7 @@ async function getBriefing(input, ctx) {
|
|
|
6886
6972
|
const briefingMaxTokens = resolvedBudget.max_tokens;
|
|
6887
6973
|
const briefingMaxMemories = resolvedBudget.max_memories;
|
|
6888
6974
|
const briefingIncludeModules = resolvedBudget.include_module_contexts;
|
|
6889
|
-
const inferred =
|
|
6975
|
+
const inferred = inferModulesFromPaths22(input.files);
|
|
6890
6976
|
const memories = [];
|
|
6891
6977
|
let searchMode = "literal";
|
|
6892
6978
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
@@ -7196,11 +7282,20 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
7196
7282
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
7197
7283
|
}
|
|
7198
7284
|
const formattedMemories = input.format === "compact" ? trimmedMemories.map((m) => ({ ...m, body: compactSummary(m.body) })) : input.format === "actions" ? trimmedMemories.map((m) => ({ ...m, body: extractActionsBriefBody2(m.body) })) : trimmedMemories;
|
|
7199
|
-
|
|
7285
|
+
let outputMemories = formattedMemories.map((m) => ({
|
|
7200
7286
|
...m,
|
|
7201
7287
|
priority: classifyMemoryPriority2(m, byId.get(m.id), input.files, input.symbols),
|
|
7202
7288
|
why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
|
|
7203
7289
|
}));
|
|
7290
|
+
if (input.format === "full") {
|
|
7291
|
+
const hasDirectHits = outputMemories.some((m) => m.priority === "must_read" || m.priority === "useful");
|
|
7292
|
+
if (hasDirectHits) {
|
|
7293
|
+
outputMemories = outputMemories.map(
|
|
7294
|
+
(m) => m.priority === "background" ? { ...m, body: `${compactSummary(m.body)}
|
|
7295
|
+
(background \u2014 full body: mem_get("${m.id}"))` } : m
|
|
7296
|
+
);
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7204
7299
|
const briefingQuality = classifyBriefingQuality(outputMemories, {
|
|
7205
7300
|
isTemplateContext,
|
|
7206
7301
|
autoContextGenerated,
|
|
@@ -7515,7 +7610,7 @@ function oneLine(value) {
|
|
|
7515
7610
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
7516
7611
|
}
|
|
7517
7612
|
function serverVersion() {
|
|
7518
|
-
return true ? "0.
|
|
7613
|
+
return true ? "0.31.0" : "dev";
|
|
7519
7614
|
}
|
|
7520
7615
|
var CodeMapInputSchema = {
|
|
7521
7616
|
file: z21.string().optional().describe("Filter to files whose path contains this substring"),
|
|
@@ -9426,7 +9521,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
9426
9521
|
};
|
|
9427
9522
|
}
|
|
9428
9523
|
var SERVER_NAME = "hivelore";
|
|
9429
|
-
var SERVER_VERSION = "0.
|
|
9524
|
+
var SERVER_VERSION = "0.31.0";
|
|
9430
9525
|
function jsonResult(data) {
|
|
9431
9526
|
return {
|
|
9432
9527
|
content: [
|
|
@@ -11972,35 +12067,20 @@ function registerMemoryHot(memory2) {
|
|
|
11972
12067
|
}
|
|
11973
12068
|
|
|
11974
12069
|
// src/commands/memory-tried.ts
|
|
11975
|
-
import { mkdir as mkdir14, writeFile as writeFile23 } from "fs/promises";
|
|
11976
12070
|
import { existsSync as existsSync45 } from "fs";
|
|
11977
12071
|
import path28 from "path";
|
|
11978
12072
|
import "commander";
|
|
11979
12073
|
import {
|
|
11980
|
-
buildFrontmatter as buildFrontmatter8,
|
|
11981
12074
|
findProjectRoot as findProjectRoot22,
|
|
11982
|
-
|
|
11983
|
-
resolveHaivePaths as resolveHaivePaths19,
|
|
11984
|
-
serializeMemory as serializeMemory20,
|
|
11985
|
-
suggestSensorSeed as suggestSensorSeed4
|
|
12075
|
+
resolveHaivePaths as resolveHaivePaths19
|
|
11986
12076
|
} from "@hivelore/core";
|
|
12077
|
+
function parseCsv4(raw) {
|
|
12078
|
+
return (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
12079
|
+
}
|
|
11987
12080
|
function registerMemoryTried(memory2) {
|
|
11988
12081
|
memory2.command("tried").description(
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
This is the most valuable type of negative knowledge. It surfaces FIRST in
|
|
11992
|
-
get_briefing so agents can't miss it. Auto-validated (no approval cycle).
|
|
11993
|
-
|
|
11994
|
-
Use this immediately when you try something and it fails.
|
|
11995
|
-
|
|
11996
|
-
Example:
|
|
11997
|
-
hivelore memory tried \\\\
|
|
11998
|
-
--what "importing X with ESM dynamic import" \\\\
|
|
11999
|
-
--why-failed "tsup bundles it as CJS, dynamic import fails at runtime" \\\\
|
|
12000
|
-
--instead "use static import in the entry file" \\\\
|
|
12001
|
-
--paths packages/cli/src/index.ts
|
|
12002
|
-
`
|
|
12003
|
-
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12082
|
+
'Record a FAILED approach \u2014 prevents repeated mistakes in future sessions.\n\n This is the most valuable type of negative knowledge. It surfaces FIRST in\n get_briefing so agents can\'t miss it. Auto-validated (no approval cycle).\n\n Use this immediately when you try something and it fails.\n\n One-shot loop close: add --sensor-pattern to validate and attach the guardrail\n in the same command (equivalent to a follow-up `sensors propose`).\n\n Example:\n hivelore memory tried \\\\\n --what "importing X with ESM dynamic import" \\\\\n --why-failed "tsup bundles it as CJS, dynamic import fails at runtime" \\\\\n --instead "use static import in the entry file" \\\\\n --paths packages/cli/src/index.ts \\\\\n --sensor-pattern "await import\\\\(" --sensor-absent "static import"\n'
|
|
12083
|
+
).requiredOption("--what <text>", "what approach was tried (short, descriptive title)").requiredOption("--why-failed <text>", "why it failed or should NOT be used (include the exact error if possible)").option("--instead <text>", "the correct approach to use instead").option("--scope <scope>", "personal | team | module", "personal").option("--module <name>", "module name (required when scope=module)").option("--tags <csv>", "comma-separated tags").option("--paths <csv>", "anchor paths, comma-separated").option("--files <csv>", "alias for --paths (matches the MCP `files` parameter)").option("--author <author>", "author email or handle").option("--sensor-pattern <regex>", "one-shot: regex matching the FAULTY usage \u2014 validates + attaches a sensor in this call").option("--sensor-absent <regex>", "one-shot: regex marking CORRECT usage nearby (excludes it from firing)").option("--sensor-severity <level>", "one-shot sensor severity: warn | block", "block").option("--sensor-message <text>", "one-shot: self-correction message shown when the sensor fires").option("--bad-example <code>", "one-shot: code snippet the sensor must fire on (validation)").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12004
12084
|
const root = findProjectRoot22(opts.dir);
|
|
12005
12085
|
const paths = resolveHaivePaths19(root);
|
|
12006
12086
|
if (!existsSync45(paths.haiveDir)) {
|
|
@@ -12008,52 +12088,56 @@ function registerMemoryTried(memory2) {
|
|
|
12008
12088
|
process.exitCode = 1;
|
|
12009
12089
|
return;
|
|
12010
12090
|
}
|
|
12011
|
-
const
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12091
|
+
const severity = opts.sensorSeverity === "warn" ? "warn" : "block";
|
|
12092
|
+
let result;
|
|
12093
|
+
try {
|
|
12094
|
+
result = await memTried(
|
|
12095
|
+
{
|
|
12096
|
+
what: opts.what,
|
|
12097
|
+
why_failed: opts.whyFailed,
|
|
12098
|
+
instead: opts.instead,
|
|
12099
|
+
// "shared" is a legacy MemoryScope alias not accepted by mem_tried — normalize to team.
|
|
12100
|
+
scope: opts.scope === "shared" ? "team" : opts.scope ?? "personal",
|
|
12101
|
+
module: opts.module,
|
|
12102
|
+
tags: parseCsv4(opts.tags),
|
|
12103
|
+
paths: parseCsv4(opts.paths ?? opts.files),
|
|
12104
|
+
author: opts.author,
|
|
12105
|
+
sensor: opts.sensorPattern ? {
|
|
12106
|
+
pattern: opts.sensorPattern,
|
|
12107
|
+
absent: opts.sensorAbsent,
|
|
12108
|
+
severity,
|
|
12109
|
+
message: opts.sensorMessage,
|
|
12110
|
+
bad_example: opts.badExample
|
|
12111
|
+
} : void 0
|
|
12112
|
+
},
|
|
12113
|
+
{ paths }
|
|
12114
|
+
);
|
|
12115
|
+
} catch (err) {
|
|
12116
|
+
ui.error(err instanceof Error ? err.message : String(err));
|
|
12032
12117
|
process.exitCode = 1;
|
|
12033
12118
|
return;
|
|
12034
12119
|
}
|
|
12035
|
-
|
|
12036
|
-
ui.
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12120
|
+
ui.success(`Recorded: ${path28.relative(root, result.file_path)}`);
|
|
12121
|
+
ui.info(`id=${result.id} type=attempt status=validated (auto-approved)`);
|
|
12122
|
+
if (result.sensor_result) {
|
|
12123
|
+
if (result.sensor_result.accepted) {
|
|
12124
|
+
ui.success(`Loop closed: sensor attached (${result.sensor_result.severity}) \u2014 the gate now refuses a repeat deterministically.`);
|
|
12125
|
+
} else {
|
|
12126
|
+
ui.warn(`Attempt saved, but the sensor was rejected (${result.sensor_result.reason ?? "rejected"}).`);
|
|
12127
|
+
if (result.sensor_result.guidance) ui.info(` ${result.sensor_result.guidance}`);
|
|
12128
|
+
}
|
|
12129
|
+
return;
|
|
12130
|
+
}
|
|
12131
|
+
if (result.proposed_sensor_seed) {
|
|
12132
|
+
ui.warn("Lesson NOT yet enforced \u2014 close the loop with `--sensor-pattern` (one-shot) or propose_sensor (MCP).");
|
|
12043
12133
|
ui.info(
|
|
12044
|
-
` candidate pattern=${JSON.stringify(
|
|
12045
|
-
);
|
|
12046
|
-
} else {
|
|
12047
|
-
ui.warn(
|
|
12048
|
-
"Lesson NOT yet enforced and no candidate pattern could be derived. Author a discriminating sensor (pattern = faulty usage, absent = correct-usage marker) via propose_sensor."
|
|
12134
|
+
` candidate pattern=${JSON.stringify(result.proposed_sensor_seed.pattern)}` + (result.proposed_sensor_seed.absent ? ` absent=${JSON.stringify(result.proposed_sensor_seed.absent)}` : "") + " \u2014 refine, then validate (silent on current code, fires on the bad example)."
|
|
12049
12135
|
);
|
|
12136
|
+
} else if (result.hint) {
|
|
12137
|
+
ui.warn(result.hint);
|
|
12050
12138
|
}
|
|
12051
12139
|
});
|
|
12052
12140
|
}
|
|
12053
|
-
function parseCsv4(value) {
|
|
12054
|
-
if (!value) return [];
|
|
12055
|
-
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
12056
|
-
}
|
|
12057
12141
|
|
|
12058
12142
|
// src/commands/memory-seed.ts
|
|
12059
12143
|
import { readFile as readFile14 } from "fs/promises";
|
|
@@ -12299,7 +12383,7 @@ ${top.length} of ${matches.length} match${matches.length === 1 ? "" : "es"}`)
|
|
|
12299
12383
|
}
|
|
12300
12384
|
|
|
12301
12385
|
// src/commands/memory-reject.ts
|
|
12302
|
-
import { writeFile as
|
|
12386
|
+
import { writeFile as writeFile23 } from "fs/promises";
|
|
12303
12387
|
import { existsSync as existsSync49 } from "fs";
|
|
12304
12388
|
import "commander";
|
|
12305
12389
|
import {
|
|
@@ -12308,7 +12392,7 @@ import {
|
|
|
12308
12392
|
recordRejection as recordRejection3,
|
|
12309
12393
|
resolveHaivePaths as resolveHaivePaths23,
|
|
12310
12394
|
saveUsageIndex as saveUsageIndex4,
|
|
12311
|
-
serializeMemory as
|
|
12395
|
+
serializeMemory as serializeMemory20
|
|
12312
12396
|
} from "@hivelore/core";
|
|
12313
12397
|
function registerMemoryReject(memory2) {
|
|
12314
12398
|
memory2.command("reject <id>").description("Record a rejection (blocks auto-promotion and lowers confidence)").option("-r, --reason <reason>", "why this memory is being rejected").option("-d, --dir <dir>", "project root").action(async (id, opts) => {
|
|
@@ -12326,9 +12410,9 @@ function registerMemoryReject(memory2) {
|
|
|
12326
12410
|
process.exitCode = 1;
|
|
12327
12411
|
return;
|
|
12328
12412
|
}
|
|
12329
|
-
await
|
|
12413
|
+
await writeFile23(
|
|
12330
12414
|
loaded.filePath,
|
|
12331
|
-
|
|
12415
|
+
serializeMemory20({
|
|
12332
12416
|
frontmatter: {
|
|
12333
12417
|
...loaded.memory.frontmatter,
|
|
12334
12418
|
status: "rejected",
|
|
@@ -12594,7 +12678,7 @@ function pad(value, width) {
|
|
|
12594
12678
|
|
|
12595
12679
|
// src/commands/memory-feedback.ts
|
|
12596
12680
|
import { existsSync as existsSync55 } from "fs";
|
|
12597
|
-
import { writeFile as
|
|
12681
|
+
import { writeFile as writeFile24 } from "fs/promises";
|
|
12598
12682
|
import "commander";
|
|
12599
12683
|
import {
|
|
12600
12684
|
applyFeedbackAdjustment as applyFeedbackAdjustment2,
|
|
@@ -12607,7 +12691,7 @@ import {
|
|
|
12607
12691
|
recommendFeedbackAdjustment as recommendFeedbackAdjustment2,
|
|
12608
12692
|
resolveHaivePaths as resolveHaivePaths28,
|
|
12609
12693
|
saveUsageIndex as saveUsageIndex6,
|
|
12610
|
-
serializeMemory as
|
|
12694
|
+
serializeMemory as serializeMemory21
|
|
12611
12695
|
} from "@hivelore/core";
|
|
12612
12696
|
function registerMemoryFeedback(memory2) {
|
|
12613
12697
|
memory2.command("feedback <id>").description(
|
|
@@ -12642,7 +12726,7 @@ function registerMemoryFeedback(memory2) {
|
|
|
12642
12726
|
const adjustedFrontmatter = applyFeedbackAdjustment2(target.memory.frontmatter, adjustment);
|
|
12643
12727
|
if (adjustedFrontmatter !== target.memory.frontmatter) {
|
|
12644
12728
|
target.memory.frontmatter = adjustedFrontmatter;
|
|
12645
|
-
await
|
|
12729
|
+
await writeFile24(target.filePath, serializeMemory21(target.memory), "utf8");
|
|
12646
12730
|
}
|
|
12647
12731
|
const impact = computeImpact4(target.memory.frontmatter, usage);
|
|
12648
12732
|
if (opts.json) {
|
|
@@ -12660,14 +12744,14 @@ function registerMemoryFeedback(memory2) {
|
|
|
12660
12744
|
}
|
|
12661
12745
|
|
|
12662
12746
|
// src/commands/memory-verify.ts
|
|
12663
|
-
import { writeFile as
|
|
12747
|
+
import { writeFile as writeFile25 } from "fs/promises";
|
|
12664
12748
|
import { existsSync as existsSync56 } from "fs";
|
|
12665
12749
|
import path36 from "path";
|
|
12666
12750
|
import "commander";
|
|
12667
12751
|
import {
|
|
12668
12752
|
findProjectRoot as findProjectRoot32,
|
|
12669
12753
|
resolveHaivePaths as resolveHaivePaths29,
|
|
12670
|
-
serializeMemory as
|
|
12754
|
+
serializeMemory as serializeMemory23,
|
|
12671
12755
|
verifyAnchor as verifyAnchor3
|
|
12672
12756
|
} from "@hivelore/core";
|
|
12673
12757
|
function registerMemoryVerify(memory2) {
|
|
@@ -12734,7 +12818,7 @@ function registerMemoryVerify(memory2) {
|
|
|
12734
12818
|
}
|
|
12735
12819
|
if (opts.update) {
|
|
12736
12820
|
const next = applyVerification2(mem, result);
|
|
12737
|
-
await
|
|
12821
|
+
await writeFile25(filePath, serializeMemory23(next), "utf8");
|
|
12738
12822
|
updated++;
|
|
12739
12823
|
}
|
|
12740
12824
|
}
|
|
@@ -12848,14 +12932,14 @@ function registerMemoryImport(memory2) {
|
|
|
12848
12932
|
|
|
12849
12933
|
// src/commands/memory-import-changelog.ts
|
|
12850
12934
|
import { existsSync as existsSync58 } from "fs";
|
|
12851
|
-
import { readFile as readFile17, mkdir as
|
|
12935
|
+
import { readFile as readFile17, mkdir as mkdir14, writeFile as writeFile26 } from "fs/promises";
|
|
12852
12936
|
import path37 from "path";
|
|
12853
12937
|
import "commander";
|
|
12854
12938
|
import {
|
|
12855
|
-
buildFrontmatter as
|
|
12939
|
+
buildFrontmatter as buildFrontmatter8,
|
|
12856
12940
|
findProjectRoot as findProjectRoot34,
|
|
12857
12941
|
resolveHaivePaths as resolveHaivePaths31,
|
|
12858
|
-
serializeMemory as
|
|
12942
|
+
serializeMemory as serializeMemory24
|
|
12859
12943
|
} from "@hivelore/core";
|
|
12860
12944
|
function parseChangelog(content) {
|
|
12861
12945
|
const entries = [];
|
|
@@ -12945,7 +13029,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
12945
13029
|
const pkgName = opts.package ?? path37.basename(path37.dirname(changelogPath));
|
|
12946
13030
|
const scope = opts.scope ?? "team";
|
|
12947
13031
|
const teamDir = path37.join(paths.memoriesDir, scope);
|
|
12948
|
-
await
|
|
13032
|
+
await mkdir14(teamDir, { recursive: true });
|
|
12949
13033
|
let saved = 0;
|
|
12950
13034
|
for (const entry of entries) {
|
|
12951
13035
|
const lines = [];
|
|
@@ -12971,7 +13055,7 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
12971
13055
|
**Action:** Update all usages of ${pkgName} if they rely on any of the above.`
|
|
12972
13056
|
);
|
|
12973
13057
|
const slug = `changelog-${pkgName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}-v${entry.version.replace(/\./g, "-")}`;
|
|
12974
|
-
const fm =
|
|
13058
|
+
const fm = buildFrontmatter8({
|
|
12975
13059
|
type: "gotcha",
|
|
12976
13060
|
slug,
|
|
12977
13061
|
scope,
|
|
@@ -12985,9 +13069,9 @@ function registerMemoryImportChangelog(memory2) {
|
|
|
12985
13069
|
paths: [path37.relative(root, changelogPath)],
|
|
12986
13070
|
topic: `changelog-${pkgName}-${entry.version}`
|
|
12987
13071
|
});
|
|
12988
|
-
await
|
|
13072
|
+
await writeFile26(
|
|
12989
13073
|
path37.join(teamDir, `${fm.id}.md`),
|
|
12990
|
-
|
|
13074
|
+
serializeMemory24({ frontmatter: fm, body: lines.join("\n") }),
|
|
12991
13075
|
"utf8"
|
|
12992
13076
|
);
|
|
12993
13077
|
console.log(ui.green(` \u2713 ${fm.id}`));
|
|
@@ -13010,7 +13094,7 @@ ${ui.bold(`Imported ${saved} changelog entr${saved === 1 ? "y" : "ies"} from ${p
|
|
|
13010
13094
|
|
|
13011
13095
|
// src/commands/memory-digest.ts
|
|
13012
13096
|
import { existsSync as existsSync59 } from "fs";
|
|
13013
|
-
import { writeFile as
|
|
13097
|
+
import { writeFile as writeFile27 } from "fs/promises";
|
|
13014
13098
|
import path38 from "path";
|
|
13015
13099
|
import "commander";
|
|
13016
13100
|
import {
|
|
@@ -13107,7 +13191,7 @@ function registerMemoryDigest(program2) {
|
|
|
13107
13191
|
const digest = lines.join("\n");
|
|
13108
13192
|
if (opts.out) {
|
|
13109
13193
|
const outPath = path38.resolve(process.cwd(), opts.out);
|
|
13110
|
-
await
|
|
13194
|
+
await writeFile27(outPath, digest, "utf8");
|
|
13111
13195
|
ui.success(`Digest written to ${opts.out} (${recent.length} memor${recent.length === 1 ? "y" : "ies"})`);
|
|
13112
13196
|
} else {
|
|
13113
13197
|
console.log(digest);
|
|
@@ -13116,22 +13200,22 @@ function registerMemoryDigest(program2) {
|
|
|
13116
13200
|
}
|
|
13117
13201
|
|
|
13118
13202
|
// src/commands/session-end.ts
|
|
13119
|
-
import { writeFile as
|
|
13203
|
+
import { writeFile as writeFile28, mkdir as mkdir15, readFile as readFile18, rm as rm2 } from "fs/promises";
|
|
13120
13204
|
import { existsSync as existsSync60 } from "fs";
|
|
13121
13205
|
import { spawn as spawn4 } from "child_process";
|
|
13122
13206
|
import path39 from "path";
|
|
13123
13207
|
import { Option as Option2 } from "commander";
|
|
13124
13208
|
import {
|
|
13125
|
-
buildFrontmatter as
|
|
13209
|
+
buildFrontmatter as buildFrontmatter9,
|
|
13126
13210
|
findProjectRoot as findProjectRoot36,
|
|
13127
13211
|
loadConfig as loadConfig7,
|
|
13128
13212
|
loadMemoriesFromDir as loadMemoriesFromDir31,
|
|
13129
13213
|
loadPreventionEvents as loadPreventionEvents2,
|
|
13130
13214
|
loadUsageIndex as loadUsageIndex26,
|
|
13131
|
-
memoryFilePath as
|
|
13215
|
+
memoryFilePath as memoryFilePath9,
|
|
13132
13216
|
renderCaughtForYou,
|
|
13133
13217
|
resolveHaivePaths as resolveHaivePaths33,
|
|
13134
|
-
serializeMemory as
|
|
13218
|
+
serializeMemory as serializeMemory25,
|
|
13135
13219
|
summarizeCaughtForYou,
|
|
13136
13220
|
writeSessionHandoff as writeSessionHandoff2
|
|
13137
13221
|
} from "@hivelore/core";
|
|
@@ -13443,7 +13527,7 @@ function registerSessionEnd(session2) {
|
|
|
13443
13527
|
paths: filesTouched.length ? filesTouched : fm.anchor.paths
|
|
13444
13528
|
}
|
|
13445
13529
|
};
|
|
13446
|
-
await
|
|
13530
|
+
await writeFile28(topicMatch.filePath, serializeMemory25({ frontmatter: newFrontmatter, body }), "utf8");
|
|
13447
13531
|
await cleanupObservations();
|
|
13448
13532
|
if (!opts.quiet) {
|
|
13449
13533
|
ui.success(`Session recap updated (revision #${revisionCount})`);
|
|
@@ -13454,7 +13538,7 @@ function registerSessionEnd(session2) {
|
|
|
13454
13538
|
return;
|
|
13455
13539
|
}
|
|
13456
13540
|
}
|
|
13457
|
-
const frontmatter =
|
|
13541
|
+
const frontmatter = buildFrontmatter9({
|
|
13458
13542
|
type: "session_recap",
|
|
13459
13543
|
slug: "recap",
|
|
13460
13544
|
scope,
|
|
@@ -13464,9 +13548,9 @@ function registerSessionEnd(session2) {
|
|
|
13464
13548
|
topic,
|
|
13465
13549
|
status: "validated"
|
|
13466
13550
|
});
|
|
13467
|
-
const file =
|
|
13468
|
-
await
|
|
13469
|
-
await
|
|
13551
|
+
const file = memoryFilePath9(paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
13552
|
+
await mkdir15(path39.dirname(file), { recursive: true });
|
|
13553
|
+
await writeFile28(file, serializeMemory25({ frontmatter, body }), "utf8");
|
|
13470
13554
|
await cleanupObservations();
|
|
13471
13555
|
if (!opts.quiet) {
|
|
13472
13556
|
ui.success(`Session recap created`);
|
|
@@ -13657,7 +13741,7 @@ function detectFormat(filePath) {
|
|
|
13657
13741
|
|
|
13658
13742
|
// src/commands/hub.ts
|
|
13659
13743
|
import { existsSync as existsSync63 } from "fs";
|
|
13660
|
-
import { mkdir as
|
|
13744
|
+
import { mkdir as mkdir16, readFile as readFile19, writeFile as writeFile29, copyFile } from "fs/promises";
|
|
13661
13745
|
import path41 from "path";
|
|
13662
13746
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
13663
13747
|
import "commander";
|
|
@@ -13667,7 +13751,7 @@ import {
|
|
|
13667
13751
|
loadMemoriesFromDir as loadMemoriesFromDir33,
|
|
13668
13752
|
resolveHaivePaths as resolveHaivePaths35,
|
|
13669
13753
|
saveConfig as saveConfig3,
|
|
13670
|
-
serializeMemory as
|
|
13754
|
+
serializeMemory as serializeMemory26
|
|
13671
13755
|
} from "@hivelore/core";
|
|
13672
13756
|
function registerHub(program2) {
|
|
13673
13757
|
const hub = program2.command("hub").description(
|
|
@@ -13678,7 +13762,7 @@ function registerHub(program2) {
|
|
|
13678
13762
|
"Initialize a new team-knowledge hub repo at <hubPath>.\n\n Creates a git repo with a .ai/ directory structure ready for shared memories.\n\n Example:\n hivelore hub init ../team-hub\n hivelore hub init /srv/git/team-knowledge\n"
|
|
13679
13763
|
).action(async (hubPath) => {
|
|
13680
13764
|
const absPath = path41.resolve(hubPath);
|
|
13681
|
-
await
|
|
13765
|
+
await mkdir16(absPath, { recursive: true });
|
|
13682
13766
|
const gitCheck = spawnSync5("git", ["rev-parse", "--git-dir"], { cwd: absPath });
|
|
13683
13767
|
if (gitCheck.status !== 0) {
|
|
13684
13768
|
const init = spawnSync5("git", ["init"], { cwd: absPath, encoding: "utf8" });
|
|
@@ -13689,8 +13773,8 @@ function registerHub(program2) {
|
|
|
13689
13773
|
}
|
|
13690
13774
|
}
|
|
13691
13775
|
const sharedDir = path41.join(absPath, ".ai", "memories", "shared");
|
|
13692
|
-
await
|
|
13693
|
-
await
|
|
13776
|
+
await mkdir16(sharedDir, { recursive: true });
|
|
13777
|
+
await writeFile29(
|
|
13694
13778
|
path41.join(absPath, ".ai", "README.md"),
|
|
13695
13779
|
`# Hivelore Team Knowledge Hub
|
|
13696
13780
|
|
|
@@ -13712,7 +13796,7 @@ hivelore hub pull # import into a project
|
|
|
13712
13796
|
`,
|
|
13713
13797
|
"utf8"
|
|
13714
13798
|
);
|
|
13715
|
-
await
|
|
13799
|
+
await writeFile29(
|
|
13716
13800
|
path41.join(absPath, ".gitignore"),
|
|
13717
13801
|
".ai/.cache/\n.ai/memories/personal/\n",
|
|
13718
13802
|
"utf8"
|
|
@@ -13765,7 +13849,7 @@ Next steps:
|
|
|
13765
13849
|
}
|
|
13766
13850
|
const projectName = path41.basename(root);
|
|
13767
13851
|
const destDir = path41.join(hubRoot, ".ai", "memories", "shared", projectName);
|
|
13768
|
-
await
|
|
13852
|
+
await mkdir16(destDir, { recursive: true });
|
|
13769
13853
|
const all = await loadMemoriesFromDir33(paths.memoriesDir);
|
|
13770
13854
|
const shared = all.filter(
|
|
13771
13855
|
({ memory: memory2 }) => memory2.frontmatter.scope === "shared" && memory2.frontmatter.status !== "rejected" && memory2.frontmatter.status !== "deprecated" && // Don't push imported memories (avoid echo loops)
|
|
@@ -13784,7 +13868,7 @@ Next steps:
|
|
|
13784
13868
|
const fm = memory2.frontmatter;
|
|
13785
13869
|
const fileName = `${fm.id}.md`;
|
|
13786
13870
|
const destPath = path41.join(destDir, fileName);
|
|
13787
|
-
await
|
|
13871
|
+
await writeFile29(destPath, serializeMemory26(memory2), "utf8");
|
|
13788
13872
|
pushed++;
|
|
13789
13873
|
}
|
|
13790
13874
|
console.log(ui.green(`\u2713 Pushed ${pushed} shared memor${pushed === 1 ? "y" : "ies"} to hub`));
|
|
@@ -13844,7 +13928,7 @@ Next steps:
|
|
|
13844
13928
|
for (const sourceName of projectDirs) {
|
|
13845
13929
|
const sourceDir = path41.join(hubSharedDir, sourceName);
|
|
13846
13930
|
const destDir = path41.join(paths.memoriesDir, "shared", sourceName);
|
|
13847
|
-
await
|
|
13931
|
+
await mkdir16(destDir, { recursive: true });
|
|
13848
13932
|
const sourceFiles = (await readdir9(sourceDir)).filter((f) => f.endsWith(".md"));
|
|
13849
13933
|
const { loadMemoriesFromDir: loadDir } = await import("@hivelore/core");
|
|
13850
13934
|
const existingInDest = await loadDir(destDir);
|
|
@@ -13905,7 +13989,7 @@ Next steps:
|
|
|
13905
13989
|
console.log(ui.dim(" Run `hivelore hub push` to publish them to the hub."));
|
|
13906
13990
|
}
|
|
13907
13991
|
void readFile19;
|
|
13908
|
-
void
|
|
13992
|
+
void writeFile29;
|
|
13909
13993
|
void saveConfig3;
|
|
13910
13994
|
});
|
|
13911
13995
|
}
|
|
@@ -13913,7 +13997,7 @@ Next steps:
|
|
|
13913
13997
|
// src/commands/stats.ts
|
|
13914
13998
|
import "commander";
|
|
13915
13999
|
import { existsSync as existsSync64 } from "fs";
|
|
13916
|
-
import { mkdir as
|
|
14000
|
+
import { mkdir as mkdir17, writeFile as writeFile30 } from "fs/promises";
|
|
13917
14001
|
import path43 from "path";
|
|
13918
14002
|
import {
|
|
13919
14003
|
aggregateUsage,
|
|
@@ -14020,7 +14104,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
14020
14104
|
ui.warn("Usage log missing or empty \u2014 report still written with partial data.");
|
|
14021
14105
|
events = [];
|
|
14022
14106
|
}
|
|
14023
|
-
await
|
|
14107
|
+
await mkdir17(path43.dirname(outAbs), { recursive: true });
|
|
14024
14108
|
const payload = {
|
|
14025
14109
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14026
14110
|
project_root: root,
|
|
@@ -14032,7 +14116,7 @@ async function writeRoiReport(paths, root, sinceRaw, outRelative) {
|
|
|
14032
14116
|
top_memory_reads: memoryHitsLeader,
|
|
14033
14117
|
roi_hints: roiHints
|
|
14034
14118
|
};
|
|
14035
|
-
await
|
|
14119
|
+
await writeFile30(outAbs, JSON.stringify(payload, null, 2), "utf8");
|
|
14036
14120
|
ui.success(`Wrote ROI / usage rollup \u2192 ${outAbs}`);
|
|
14037
14121
|
}
|
|
14038
14122
|
async function renderMemoryHits(paths, opts) {
|
|
@@ -14210,7 +14294,7 @@ function summarize(name, t0, payload, notes) {
|
|
|
14210
14294
|
|
|
14211
14295
|
// src/commands/benchmark.ts
|
|
14212
14296
|
import { existsSync as existsSync65 } from "fs";
|
|
14213
|
-
import { readdir as readdir7, readFile as readFile20, writeFile as
|
|
14297
|
+
import { readdir as readdir7, readFile as readFile20, writeFile as writeFile31 } from "fs/promises";
|
|
14214
14298
|
import path44 from "path";
|
|
14215
14299
|
import "commander";
|
|
14216
14300
|
import { estimateTokens as estimateTokens4, findProjectRoot as findProjectRoot41 } from "@hivelore/core";
|
|
@@ -14227,7 +14311,7 @@ function registerBenchmark(program2) {
|
|
|
14227
14311
|
const markdown = renderMarkdown(root, summary, rows);
|
|
14228
14312
|
if (opts.out) {
|
|
14229
14313
|
const outFile = path44.isAbsolute(opts.out) ? opts.out : path44.join(root, opts.out);
|
|
14230
|
-
await
|
|
14314
|
+
await writeFile31(outFile, markdown, "utf8");
|
|
14231
14315
|
ui.success(`wrote ${path44.relative(process.cwd(), outFile)}`);
|
|
14232
14316
|
return;
|
|
14233
14317
|
}
|
|
@@ -14356,7 +14440,7 @@ function escapeRegExp(value) {
|
|
|
14356
14440
|
}
|
|
14357
14441
|
|
|
14358
14442
|
// src/commands/eval.ts
|
|
14359
|
-
import { mkdir as
|
|
14443
|
+
import { mkdir as mkdir18, readFile as readFile21, writeFile as writeFile33 } from "fs/promises";
|
|
14360
14444
|
import { existsSync as existsSync66 } from "fs";
|
|
14361
14445
|
import path45 from "path";
|
|
14362
14446
|
import "commander";
|
|
@@ -14467,8 +14551,8 @@ function registerEval(program2) {
|
|
|
14467
14551
|
report,
|
|
14468
14552
|
gate_precision: gatePrecision
|
|
14469
14553
|
};
|
|
14470
|
-
await
|
|
14471
|
-
await
|
|
14554
|
+
await mkdir18(path45.dirname(baselineFile), { recursive: true });
|
|
14555
|
+
await writeFile33(baselineFile, JSON.stringify(snapshot, null, 2), "utf8");
|
|
14472
14556
|
if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path45.relative(root, baselineFile)}`);
|
|
14473
14557
|
}
|
|
14474
14558
|
let delta = null;
|
|
@@ -14522,7 +14606,7 @@ function registerEval(program2) {
|
|
|
14522
14606
|
const md = renderMarkdown2(root, k, resolvedSpec, report, gatePrecision, authoredScore);
|
|
14523
14607
|
if (opts.out) {
|
|
14524
14608
|
const outFile = path45.isAbsolute(opts.out) ? opts.out : path45.join(root, opts.out);
|
|
14525
|
-
await
|
|
14609
|
+
await writeFile33(outFile, md, "utf8");
|
|
14526
14610
|
ui.success(`wrote ${path45.relative(process.cwd(), outFile)}`);
|
|
14527
14611
|
} else {
|
|
14528
14612
|
console.log(md);
|
|
@@ -14739,21 +14823,21 @@ function renderMarkdown2(root, k, resolved, report, gatePrecision, authoredScore
|
|
|
14739
14823
|
}
|
|
14740
14824
|
|
|
14741
14825
|
// src/commands/memory-suggest.ts
|
|
14742
|
-
import { mkdir as
|
|
14826
|
+
import { mkdir as mkdir19, writeFile as writeFile34 } from "fs/promises";
|
|
14743
14827
|
import { existsSync as existsSync67 } from "fs";
|
|
14744
14828
|
import path46 from "path";
|
|
14745
14829
|
import "commander";
|
|
14746
14830
|
import {
|
|
14747
14831
|
aggregateUsage as aggregateUsage2,
|
|
14748
|
-
buildFrontmatter as
|
|
14832
|
+
buildFrontmatter as buildFrontmatter10,
|
|
14749
14833
|
findProjectRoot as findProjectRoot43,
|
|
14750
14834
|
loadConfig as loadConfig11,
|
|
14751
14835
|
loadMemoriesFromDir as loadMemoriesFromDir35,
|
|
14752
|
-
memoryFilePath as
|
|
14836
|
+
memoryFilePath as memoryFilePath10,
|
|
14753
14837
|
parseSince as parseSince2,
|
|
14754
14838
|
readUsageEvents as readUsageEvents3,
|
|
14755
14839
|
resolveHaivePaths as resolveHaivePaths39,
|
|
14756
|
-
serializeMemory as
|
|
14840
|
+
serializeMemory as serializeMemory27
|
|
14757
14841
|
} from "@hivelore/core";
|
|
14758
14842
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set([
|
|
14759
14843
|
"mem_search",
|
|
@@ -14831,7 +14915,7 @@ function registerMemorySuggest(memory2) {
|
|
|
14831
14915
|
skipped.push({ query: s.query, reason: `similar memory already exists (${dup.memory.frontmatter.id})` });
|
|
14832
14916
|
continue;
|
|
14833
14917
|
}
|
|
14834
|
-
const fm =
|
|
14918
|
+
const fm = buildFrontmatter10({
|
|
14835
14919
|
type: s.inferred_type,
|
|
14836
14920
|
slug,
|
|
14837
14921
|
scope,
|
|
@@ -14841,13 +14925,13 @@ function registerMemorySuggest(memory2) {
|
|
|
14841
14925
|
status
|
|
14842
14926
|
});
|
|
14843
14927
|
const body = renderTemplate(s, fm.id, status);
|
|
14844
|
-
const file =
|
|
14845
|
-
await
|
|
14928
|
+
const file = memoryFilePath10(paths, fm.scope, fm.id, fm.module);
|
|
14929
|
+
await mkdir19(path46.dirname(file), { recursive: true });
|
|
14846
14930
|
if (existsSync67(file)) {
|
|
14847
14931
|
skipped.push({ query: s.query, reason: `file already exists at ${path46.relative(root, file)}` });
|
|
14848
14932
|
continue;
|
|
14849
14933
|
}
|
|
14850
|
-
await
|
|
14934
|
+
await writeFile34(file, serializeMemory27({ frontmatter: fm, body }), "utf8");
|
|
14851
14935
|
created.push({ id: fm.id, file: path46.relative(root, file), query: s.query });
|
|
14852
14936
|
}
|
|
14853
14937
|
if (opts.json) {
|
|
@@ -14947,7 +15031,7 @@ function truncate2(text, max) {
|
|
|
14947
15031
|
|
|
14948
15032
|
// src/commands/memory-archive.ts
|
|
14949
15033
|
import { existsSync as existsSync68 } from "fs";
|
|
14950
|
-
import { writeFile as
|
|
15034
|
+
import { writeFile as writeFile35 } from "fs/promises";
|
|
14951
15035
|
import path47 from "path";
|
|
14952
15036
|
import "commander";
|
|
14953
15037
|
import {
|
|
@@ -14958,7 +15042,7 @@ import {
|
|
|
14958
15042
|
loadMemoriesFromDir as loadMemoriesFromDir36,
|
|
14959
15043
|
loadUsageIndex as loadUsageIndex29,
|
|
14960
15044
|
resolveHaivePaths as resolveHaivePaths40,
|
|
14961
|
-
serializeMemory as
|
|
15045
|
+
serializeMemory as serializeMemory28
|
|
14962
15046
|
} from "@hivelore/core";
|
|
14963
15047
|
var MS_PER_DAY2 = 24 * 60 * 60 * 1e3;
|
|
14964
15048
|
function registerMemoryArchive(memory2) {
|
|
@@ -15041,7 +15125,7 @@ function registerMemoryArchive(memory2) {
|
|
|
15041
15125
|
if (!found) continue;
|
|
15042
15126
|
const fm = { ...found.memory.frontmatter, status: "deprecated" };
|
|
15043
15127
|
try {
|
|
15044
|
-
await
|
|
15128
|
+
await writeFile35(c.filePath, serializeMemory28({ frontmatter: fm, body: found.memory.body }), "utf8");
|
|
15045
15129
|
archived++;
|
|
15046
15130
|
} catch (err) {
|
|
15047
15131
|
if (!opts.json) {
|
|
@@ -15068,7 +15152,7 @@ function parseDays(input) {
|
|
|
15068
15152
|
|
|
15069
15153
|
// src/commands/doctor.ts
|
|
15070
15154
|
import { existsSync as existsSync69, statSync as statSync2 } from "fs";
|
|
15071
|
-
import { readFile as readFile23, stat, writeFile as
|
|
15155
|
+
import { readFile as readFile23, stat, writeFile as writeFile36 } from "fs/promises";
|
|
15072
15156
|
import path48 from "path";
|
|
15073
15157
|
import { execFileSync, execSync as execSync4 } from "child_process";
|
|
15074
15158
|
import "commander";
|
|
@@ -15099,13 +15183,17 @@ function registerDoctor(program2) {
|
|
|
15099
15183
|
const repairs = [];
|
|
15100
15184
|
const config = await loadConfig13(paths);
|
|
15101
15185
|
if (!existsSync69(paths.haiveDir)) {
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
}
|
|
15108
|
-
|
|
15186
|
+
if (opts.json) {
|
|
15187
|
+
console.log(JSON.stringify({
|
|
15188
|
+
initialized: false,
|
|
15189
|
+
findings: [{ severity: "error", code: "not-initialized", message: ".ai/ directory missing \u2014 Hivelore is not initialized in this project.", fix: "hivelore init" }]
|
|
15190
|
+
}, null, 2));
|
|
15191
|
+
} else {
|
|
15192
|
+
ui.error(`Hivelore is not initialized at ${root} (no .ai/ directory).`);
|
|
15193
|
+
ui.info("Run `hivelore init` to set it up \u2014 everything it writes is removable (.ai/ + bridge files).");
|
|
15194
|
+
}
|
|
15195
|
+
process.exitCode = 1;
|
|
15196
|
+
return;
|
|
15109
15197
|
}
|
|
15110
15198
|
if (opts.fix && !opts.dryRun) {
|
|
15111
15199
|
repairs.push(
|
|
@@ -15127,8 +15215,8 @@ function registerDoctor(program2) {
|
|
|
15127
15215
|
fix: "hivelore init"
|
|
15128
15216
|
});
|
|
15129
15217
|
} else {
|
|
15130
|
-
const { readFile:
|
|
15131
|
-
const content = await
|
|
15218
|
+
const { readFile: readFile30 } = await import("fs/promises");
|
|
15219
|
+
const content = await readFile30(paths.projectContext, "utf8");
|
|
15132
15220
|
const isTemplate = content.includes("TODO \u2014 high-level overview") || content.includes("Generated by `hivelore init`");
|
|
15133
15221
|
if (isTemplate) {
|
|
15134
15222
|
findings.push({
|
|
@@ -15378,8 +15466,8 @@ function registerDoctor(program2) {
|
|
|
15378
15466
|
let hasClaudeEnforcement = false;
|
|
15379
15467
|
if (existsSync69(claudeSettings)) {
|
|
15380
15468
|
try {
|
|
15381
|
-
const { readFile:
|
|
15382
|
-
const raw = await
|
|
15469
|
+
const { readFile: readFile30 } = await import("fs/promises");
|
|
15470
|
+
const raw = await readFile30(claudeSettings, "utf8");
|
|
15383
15471
|
hasClaudeEnforcement = raw.includes("hivelore enforce session-start") && raw.includes("hivelore enforce pre-tool-use");
|
|
15384
15472
|
} catch {
|
|
15385
15473
|
hasClaudeEnforcement = false;
|
|
@@ -15402,7 +15490,7 @@ function registerDoctor(program2) {
|
|
|
15402
15490
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `hivelore init` without --manual)."
|
|
15403
15491
|
});
|
|
15404
15492
|
}
|
|
15405
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
15493
|
+
findings.push(...await collectInstallFindings(root, "0.31.0"));
|
|
15406
15494
|
findings.push(...await collectToolchainFindings(root));
|
|
15407
15495
|
try {
|
|
15408
15496
|
const legacyRaw = execSync4("haive-mcp --version", {
|
|
@@ -15410,7 +15498,7 @@ function registerDoctor(program2) {
|
|
|
15410
15498
|
timeout: 3e3,
|
|
15411
15499
|
stdio: ["ignore", "pipe", "ignore"]
|
|
15412
15500
|
}).trim();
|
|
15413
|
-
const cliVersion = "0.
|
|
15501
|
+
const cliVersion = "0.31.0";
|
|
15414
15502
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
15415
15503
|
findings.push({
|
|
15416
15504
|
severity: "warn",
|
|
@@ -15439,7 +15527,7 @@ npm uninstall -g @hivelore/mcp`
|
|
|
15439
15527
|
staleConfigs.push(path48.relative(root, cfgPath));
|
|
15440
15528
|
if (opts.fix && !opts.dryRun) {
|
|
15441
15529
|
const updated = raw.replace(/"command"\s*:\s*"haive-mcp"/g, '"command": "hivelore"').replace(/"args"\s*:\s*\[\]/g, '"args": ["mcp", "--stdio"]');
|
|
15442
|
-
await
|
|
15530
|
+
await writeFile36(cfgPath, updated, "utf8");
|
|
15443
15531
|
}
|
|
15444
15532
|
}
|
|
15445
15533
|
} catch {
|
|
@@ -16497,7 +16585,7 @@ function registerMemoryConflictCandidates(memory2) {
|
|
|
16497
16585
|
// src/commands/enforce.ts
|
|
16498
16586
|
import { execFile as execFile3, execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
16499
16587
|
import { existsSync as existsSync76, statSync as statSync3 } from "fs";
|
|
16500
|
-
import { chmod as chmod2, mkdir as
|
|
16588
|
+
import { chmod as chmod2, mkdir as mkdir20, readFile as readFile24, readdir as readdir8, rm as rm3, writeFile as writeFile37 } from "fs/promises";
|
|
16501
16589
|
import path54 from "path";
|
|
16502
16590
|
import { promisify as promisify3 } from "util";
|
|
16503
16591
|
import "commander";
|
|
@@ -16514,6 +16602,7 @@ import {
|
|
|
16514
16602
|
isFreshIsoDate,
|
|
16515
16603
|
isRetiredMemory as isRetiredMemory3,
|
|
16516
16604
|
loadConfig as loadConfig15,
|
|
16605
|
+
detectAgentContext,
|
|
16517
16606
|
loadMemoriesFromDir as loadMemoriesFromDir40,
|
|
16518
16607
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths6,
|
|
16519
16608
|
readRecentBriefingMarker,
|
|
@@ -16538,7 +16627,7 @@ function registerEnforce(program2) {
|
|
|
16538
16627
|
enforce.command("install").description("Install Hivelore enforcement across MCP config, git hooks, CI template, and supported client hooks.").option("-d, --dir <dir>", "project root").option("--no-git", "skip git pre-commit/pre-push enforcement hooks").option("--no-claude", "skip Claude Code hooks").option("--no-ci", "skip GitHub Actions enforcement workflow").action(async (opts) => {
|
|
16539
16628
|
const root = findProjectRoot52(opts.dir);
|
|
16540
16629
|
const paths = resolveHaivePaths48(root);
|
|
16541
|
-
await
|
|
16630
|
+
await mkdir20(paths.haiveDir, { recursive: true });
|
|
16542
16631
|
const current = await loadConfig15(paths);
|
|
16543
16632
|
await saveConfig4(paths, {
|
|
16544
16633
|
...current,
|
|
@@ -16606,8 +16695,18 @@ function registerEnforce(program2) {
|
|
|
16606
16695
|
});
|
|
16607
16696
|
enforce.command("finish").alias("completion").description(
|
|
16608
16697
|
"Final agent-exit gate: verify the git sync/release protocol before reporting a task done."
|
|
16609
|
-
).option("-d, --dir <dir>", "project root").option("--explain", "group findings by blocking/review/info and show repair commands", false).option("--json", "emit JSON", false).action(async (opts) => {
|
|
16610
|
-
|
|
16698
|
+
).option("-d, --dir <dir>", "project root").option("--explain", "group findings by blocking/review/info and show repair commands", false).option("--wait", "poll GitHub Actions until the runs for HEAD complete instead of failing on pending CI", false).option("--wait-timeout <minutes>", "max minutes to wait for CI with --wait", "15").option("--json", "emit JSON", false).action(async (opts) => {
|
|
16699
|
+
let report = await buildFinishReport(opts.dir);
|
|
16700
|
+
if (opts.wait) {
|
|
16701
|
+
const WAIT_CODES = /* @__PURE__ */ new Set(["github-actions-pending", "github-actions-runs-missing"]);
|
|
16702
|
+
const deadline = Date.now() + Math.max(1, Number(opts.waitTimeout ?? 15)) * 6e4;
|
|
16703
|
+
const onlyWaitingOnCi = (r) => r.should_block && r.findings.some((f) => f.severity === "error" && WAIT_CODES.has(f.code)) && !r.findings.some((f) => f.severity === "error" && !WAIT_CODES.has(f.code));
|
|
16704
|
+
while (onlyWaitingOnCi(report) && Date.now() < deadline) {
|
|
16705
|
+
if (!opts.json) ui.info("GitHub Actions still running for HEAD \u2014 rechecking in 20s (--wait)\u2026");
|
|
16706
|
+
await new Promise((resolve) => setTimeout(resolve, 2e4));
|
|
16707
|
+
report = await buildFinishReport(opts.dir);
|
|
16708
|
+
}
|
|
16709
|
+
}
|
|
16611
16710
|
printReport(report, Boolean(opts.json), Boolean(opts.explain));
|
|
16612
16711
|
if (report.should_block) {
|
|
16613
16712
|
if (!opts.json) printNextRequiredAction(report);
|
|
@@ -16630,7 +16729,7 @@ function registerEnforce(program2) {
|
|
|
16630
16729
|
if (!root) return;
|
|
16631
16730
|
const paths = resolveHaivePaths48(root);
|
|
16632
16731
|
if (!existsSync76(paths.haiveDir)) return;
|
|
16633
|
-
await
|
|
16732
|
+
await mkdir20(paths.runtimeDir, { recursive: true });
|
|
16634
16733
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
16635
16734
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this Hivelore-initialized project.";
|
|
16636
16735
|
await applyLightweightRepairs(root, paths);
|
|
@@ -17031,6 +17130,8 @@ async function runWithEnforcement(command, args, opts) {
|
|
|
17031
17130
|
HAIVE_SESSION_ID: sessionId,
|
|
17032
17131
|
HAIVE_BRIEFING_FILE: briefingFile,
|
|
17033
17132
|
HAIVE_ENFORCEMENT: "strict",
|
|
17133
|
+
HAIVE_AGENT: "1",
|
|
17134
|
+
// wrapped process is an agent — process gates bind it (detectAgentContext)
|
|
17034
17135
|
HAIVE_TOOL_PROFILE: process.env.HAIVE_TOOL_PROFILE ?? "enforcement"
|
|
17035
17136
|
}
|
|
17036
17137
|
});
|
|
@@ -17072,7 +17173,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
17072
17173
|
memoryIds: briefing.memories.map((m) => m.id)
|
|
17073
17174
|
});
|
|
17074
17175
|
const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
|
|
17075
|
-
await
|
|
17176
|
+
await mkdir20(dir, { recursive: true });
|
|
17076
17177
|
const file = path54.join(dir, `${sessionId}.md`);
|
|
17077
17178
|
const parts = [
|
|
17078
17179
|
"# Hivelore Briefing",
|
|
@@ -17091,7 +17192,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
17091
17192
|
if (briefing.setup_warnings.length > 0) {
|
|
17092
17193
|
parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
|
|
17093
17194
|
}
|
|
17094
|
-
await
|
|
17195
|
+
await writeFile37(file, parts.join("\n") + "\n", "utf8");
|
|
17095
17196
|
return file;
|
|
17096
17197
|
}
|
|
17097
17198
|
var PRODUCTION_CODE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|swift|rb|php|cs|cpp|cc|c|h|hpp|vue|svelte)$/i;
|
|
@@ -17177,7 +17278,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
17177
17278
|
findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
|
|
17178
17279
|
});
|
|
17179
17280
|
}
|
|
17180
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
17281
|
+
findings.push(...await inspectIntegrationVersions(root, "0.31.0"));
|
|
17181
17282
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
17182
17283
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
17183
17284
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
|
|
@@ -17227,24 +17328,44 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
17227
17328
|
const changed = await getChangedFiles(root, stage).catch(() => []);
|
|
17228
17329
|
findings.push(...await checkBootstrapComplete(paths, config, changed.some(looksLikeProductionCode)));
|
|
17229
17330
|
}
|
|
17230
|
-
const
|
|
17331
|
+
const agentContext = detectAgentContext();
|
|
17332
|
+
const relaxForHuman = stage !== "ci" && !agentContext.agent && (config.enforcement?.humanCommits ?? "relaxed") === "relaxed";
|
|
17333
|
+
let effectiveFindings = findings;
|
|
17334
|
+
if (relaxForHuman) {
|
|
17335
|
+
const PROCESS_GATE_CODES = /* @__PURE__ */ new Set([
|
|
17336
|
+
"briefing-missing",
|
|
17337
|
+
"session-recap-missing",
|
|
17338
|
+
"decision-coverage-missing",
|
|
17339
|
+
"bootstrap-incomplete"
|
|
17340
|
+
]);
|
|
17341
|
+
effectiveFindings = findings.map(
|
|
17342
|
+
(f) => f.severity === "error" && PROCESS_GATE_CODES.has(f.code) ? {
|
|
17343
|
+
...f,
|
|
17344
|
+
severity: "warn",
|
|
17345
|
+
impact: 5,
|
|
17346
|
+
message: `${f.message} (relaxed to a warning: no agent harness detected, so this human commit is not bound by agent process gates \u2014 set enforcement.humanCommits="strict" to change that)`
|
|
17347
|
+
} : f
|
|
17348
|
+
);
|
|
17349
|
+
}
|
|
17350
|
+
const score = buildScore(effectiveFindings, config.enforcement?.scoreThreshold);
|
|
17231
17351
|
if (score.score < score.threshold) {
|
|
17232
|
-
|
|
17352
|
+
effectiveFindings = [...effectiveFindings, {
|
|
17233
17353
|
severity: "error",
|
|
17234
17354
|
code: "enforcement-score-below-threshold",
|
|
17235
17355
|
message: `Enforcement score ${score.score}% is below required threshold ${score.threshold}%.`,
|
|
17236
17356
|
fix: "Load the relevant briefing, address policy findings, then rerun `hivelore enforce check`.",
|
|
17237
17357
|
impact: 0
|
|
17238
|
-
}
|
|
17358
|
+
}];
|
|
17239
17359
|
}
|
|
17240
|
-
const hasErrors =
|
|
17360
|
+
const hasErrors = effectiveFindings.some((f) => f.severity === "error");
|
|
17241
17361
|
return withCategories({
|
|
17242
17362
|
root,
|
|
17243
17363
|
initialized,
|
|
17244
17364
|
mode,
|
|
17245
|
-
|
|
17365
|
+
actor: agentContext.agent ? `agent (${agentContext.signals.join(", ")})` : relaxForHuman ? "human \u2014 process gates relaxed" : "human \u2014 strict (enforcement.humanCommits)",
|
|
17366
|
+
score: buildScore(effectiveFindings, config.enforcement?.scoreThreshold),
|
|
17246
17367
|
should_block: mode === "strict" && hasErrors,
|
|
17247
|
-
findings
|
|
17368
|
+
findings: effectiveFindings
|
|
17248
17369
|
});
|
|
17249
17370
|
}
|
|
17250
17371
|
function withCategories(report) {
|
|
@@ -17405,14 +17526,39 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
17405
17526
|
const reviewWarnings = result.warnings.filter(
|
|
17406
17527
|
(w) => w.level === "review" && !w.reasons.includes("sensor")
|
|
17407
17528
|
);
|
|
17529
|
+
const REVIEW_SEEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
17530
|
+
const reviewSeenFile = path54.join(paths.runtimeDir, "enforcement", "review-seen.json");
|
|
17531
|
+
let reviewSeen = {};
|
|
17532
|
+
try {
|
|
17533
|
+
reviewSeen = JSON.parse(await readFile24(reviewSeenFile, "utf8"));
|
|
17534
|
+
} catch {
|
|
17535
|
+
}
|
|
17536
|
+
const now = Date.now();
|
|
17537
|
+
const isFreshlySeen = (id) => {
|
|
17538
|
+
const at = Date.parse(reviewSeen[id] ?? "");
|
|
17539
|
+
return Number.isFinite(at) && now - at < REVIEW_SEEN_TTL_MS;
|
|
17540
|
+
};
|
|
17541
|
+
const newWarnings = reviewWarnings.filter((w) => !isFreshlySeen(w.id));
|
|
17542
|
+
const repeatCount = reviewWarnings.length - newWarnings.length;
|
|
17408
17543
|
const reviewFinding = reviewWarnings.length > 0 ? [{
|
|
17409
17544
|
severity: "warn",
|
|
17410
17545
|
code: "anti-pattern-review",
|
|
17411
|
-
message: `${reviewWarnings.length} documented lesson(s) plausibly match this diff \u2014 review before committing
|
|
17546
|
+
message: `${reviewWarnings.length} documented lesson(s) plausibly match this diff \u2014 review before committing` + (newWarnings.length > 0 ? `: ${newWarnings.slice(0, 3).map((w) => `${w.id} (${w.reasons.join("+")})`).join(", ")}` + (newWarnings.length > 3 ? ", \u2026" : "") : "") + (repeatCount > 0 ? ` (+${repeatCount} shown in the last 24h \u2014 \`hivelore precommit\` lists all)` : "") + ".",
|
|
17412
17547
|
fix: "Run `hivelore precommit` for the matched lines and repair commands; update the code, or retire the memory if it no longer applies.",
|
|
17413
17548
|
memory_ids: reviewWarnings.slice(0, 10).map((w) => w.id),
|
|
17414
17549
|
impact: 5
|
|
17415
17550
|
}] : [];
|
|
17551
|
+
if (reviewWarnings.length > 0) {
|
|
17552
|
+
try {
|
|
17553
|
+
for (const w of reviewWarnings) reviewSeen[w.id] = new Date(now).toISOString();
|
|
17554
|
+
for (const [id, at] of Object.entries(reviewSeen)) {
|
|
17555
|
+
if (!Number.isFinite(Date.parse(at)) || now - Date.parse(at) >= REVIEW_SEEN_TTL_MS) delete reviewSeen[id];
|
|
17556
|
+
}
|
|
17557
|
+
await mkdir20(path54.dirname(reviewSeenFile), { recursive: true });
|
|
17558
|
+
await writeFile37(reviewSeenFile, JSON.stringify(reviewSeen, null, 2), "utf8");
|
|
17559
|
+
} catch {
|
|
17560
|
+
}
|
|
17561
|
+
}
|
|
17416
17562
|
if (!result.should_block) {
|
|
17417
17563
|
return [
|
|
17418
17564
|
{
|
|
@@ -17545,7 +17691,7 @@ function isGeneratedArtifactStatusLine(line) {
|
|
|
17545
17691
|
}
|
|
17546
17692
|
async function cleanupRuntimeDir(runtimeDir) {
|
|
17547
17693
|
let removed = 0;
|
|
17548
|
-
await
|
|
17694
|
+
await mkdir20(runtimeDir, { recursive: true });
|
|
17549
17695
|
const entries = await readdir8(runtimeDir, { withFileTypes: true }).catch(() => []);
|
|
17550
17696
|
for (const entry of entries) {
|
|
17551
17697
|
if (entry.name === ".gitignore" || entry.name === "README.md") continue;
|
|
@@ -17556,9 +17702,9 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
17556
17702
|
await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
17557
17703
|
removed++;
|
|
17558
17704
|
}
|
|
17559
|
-
await
|
|
17705
|
+
await writeFile37(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
17560
17706
|
if (!existsSync76(path54.join(runtimeDir, "README.md"))) {
|
|
17561
|
-
await
|
|
17707
|
+
await writeFile37(
|
|
17562
17708
|
path54.join(runtimeDir, "README.md"),
|
|
17563
17709
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. Hivelore cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
17564
17710
|
"utf8"
|
|
@@ -17568,14 +17714,14 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
17568
17714
|
}
|
|
17569
17715
|
async function cleanupCacheDir(cacheDir) {
|
|
17570
17716
|
let removed = 0;
|
|
17571
|
-
await
|
|
17717
|
+
await mkdir20(cacheDir, { recursive: true });
|
|
17572
17718
|
const entries = await readdir8(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
17573
17719
|
for (const entry of entries) {
|
|
17574
17720
|
if (entry.name === ".gitignore") continue;
|
|
17575
17721
|
await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
17576
17722
|
removed++;
|
|
17577
17723
|
}
|
|
17578
|
-
await
|
|
17724
|
+
await writeFile37(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
17579
17725
|
return removed;
|
|
17580
17726
|
}
|
|
17581
17727
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -18046,7 +18192,7 @@ async function installGitEnforcement(root) {
|
|
|
18046
18192
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
18047
18193
|
return;
|
|
18048
18194
|
}
|
|
18049
|
-
await
|
|
18195
|
+
await mkdir20(hooksDir, { recursive: true });
|
|
18050
18196
|
const resolveCli = `_hivelore() {
|
|
18051
18197
|
if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
|
|
18052
18198
|
elif command -v haive >/dev/null 2>&1; then haive "$@"
|
|
@@ -18084,14 +18230,14 @@ _hivelore enforce commit-msg "$1" --dir . || exit $?
|
|
|
18084
18230
|
if (existsSync76(file)) {
|
|
18085
18231
|
const current = await readFile24(file, "utf8").catch(() => "");
|
|
18086
18232
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
18087
|
-
await
|
|
18233
|
+
await writeFile37(file, hook.body, "utf8");
|
|
18088
18234
|
} else {
|
|
18089
|
-
await
|
|
18235
|
+
await writeFile37(file, `${current.trimEnd()}
|
|
18090
18236
|
|
|
18091
18237
|
${hook.body}`, "utf8");
|
|
18092
18238
|
}
|
|
18093
18239
|
} else {
|
|
18094
|
-
await
|
|
18240
|
+
await writeFile37(file, hook.body, "utf8");
|
|
18095
18241
|
}
|
|
18096
18242
|
await chmod2(file, 493);
|
|
18097
18243
|
}
|
|
@@ -18099,12 +18245,12 @@ ${hook.body}`, "utf8");
|
|
|
18099
18245
|
}
|
|
18100
18246
|
async function installCiEnforcement(root) {
|
|
18101
18247
|
const workflowPath = path54.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
18102
|
-
await
|
|
18248
|
+
await mkdir20(path54.dirname(workflowPath), { recursive: true });
|
|
18103
18249
|
if (existsSync76(workflowPath)) {
|
|
18104
18250
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
18105
18251
|
return;
|
|
18106
18252
|
}
|
|
18107
|
-
await
|
|
18253
|
+
await writeFile37(workflowPath, `name: haive-enforcement
|
|
18108
18254
|
|
|
18109
18255
|
on:
|
|
18110
18256
|
pull_request:
|
|
@@ -18138,7 +18284,7 @@ function printReport(report, json, explain = false) {
|
|
|
18138
18284
|
console.log(JSON.stringify(report, null, 2));
|
|
18139
18285
|
return;
|
|
18140
18286
|
}
|
|
18141
|
-
console.log(ui.bold(`Hivelore enforcement \u2014 ${report.mode}`));
|
|
18287
|
+
console.log(ui.bold(`Hivelore enforcement \u2014 ${report.mode}${report.actor ? ` \xB7 ${report.actor}` : ""}`));
|
|
18142
18288
|
console.log(ui.dim(` root: ${report.root}`));
|
|
18143
18289
|
console.log(ui.dim(` score: ${report.score.score}% / threshold ${report.score.threshold}%`));
|
|
18144
18290
|
if (explain) {
|
|
@@ -18310,6 +18456,115 @@ function runCommand4(cmd, args, cwd) {
|
|
|
18310
18456
|
});
|
|
18311
18457
|
}
|
|
18312
18458
|
|
|
18459
|
+
// src/commands/release.ts
|
|
18460
|
+
import { existsSync as existsSync77 } from "fs";
|
|
18461
|
+
import { readFile as readFile25, writeFile as writeFile38 } from "fs/promises";
|
|
18462
|
+
import path55 from "path";
|
|
18463
|
+
import "commander";
|
|
18464
|
+
import { findProjectRoot as findProjectRoot53 } from "@hivelore/core";
|
|
18465
|
+
import { execFile as execFile4 } from "child_process";
|
|
18466
|
+
import { promisify as promisify4 } from "util";
|
|
18467
|
+
var exec2 = promisify4(execFile4);
|
|
18468
|
+
var VERSION_FILES2 = [
|
|
18469
|
+
"package.json",
|
|
18470
|
+
"packages/core/package.json",
|
|
18471
|
+
"packages/cli/package.json",
|
|
18472
|
+
"packages/mcp/package.json",
|
|
18473
|
+
"packages/embeddings/package.json"
|
|
18474
|
+
];
|
|
18475
|
+
async function readCurrentVersion(root) {
|
|
18476
|
+
const pkg = JSON.parse(await readFile25(path55.join(root, "package.json"), "utf8"));
|
|
18477
|
+
if (!pkg.version) throw new Error("Root package.json has no version field.");
|
|
18478
|
+
return pkg.version;
|
|
18479
|
+
}
|
|
18480
|
+
function nextVersion(current, spec) {
|
|
18481
|
+
if (/^\d+\.\d+\.\d+$/.test(spec)) return spec;
|
|
18482
|
+
const m = current.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
18483
|
+
if (!m) throw new Error(`Current version "${current}" is not X.Y.Z \u2014 pass an explicit version.`);
|
|
18484
|
+
const [major, minor, patch] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
18485
|
+
if (spec === "patch") return `${major}.${minor}.${patch + 1}`;
|
|
18486
|
+
if (spec === "minor") return `${major}.${minor + 1}.0`;
|
|
18487
|
+
if (spec === "major") return `${major + 1}.0.0`;
|
|
18488
|
+
throw new Error(`Unknown bump "${spec}" \u2014 use patch | minor | major | X.Y.Z.`);
|
|
18489
|
+
}
|
|
18490
|
+
function registerRelease(program2) {
|
|
18491
|
+
const release = program2.command("release").description("Release protocol helpers: lockstep version bump + CHANGELOG scaffold, then tag + push.");
|
|
18492
|
+
release.command("bump <version>").description("Bump root + core/cli/mcp/embeddings in lockstep (patch|minor|major|X.Y.Z) and scaffold the CHANGELOG section.").option("--title <text>", "CHANGELOG section title (after the version)").option("-d, --dir <dir>", "project root").action(async (spec, opts) => {
|
|
18493
|
+
const root = findProjectRoot53(opts.dir);
|
|
18494
|
+
const current = await readCurrentVersion(root);
|
|
18495
|
+
const next = nextVersion(current, spec);
|
|
18496
|
+
for (const rel of VERSION_FILES2) {
|
|
18497
|
+
const file = path55.join(root, rel);
|
|
18498
|
+
if (!existsSync77(file)) {
|
|
18499
|
+
ui.warn(`skip ${rel} (missing)`);
|
|
18500
|
+
continue;
|
|
18501
|
+
}
|
|
18502
|
+
const raw = await readFile25(file, "utf8");
|
|
18503
|
+
const updated = raw.replace(`"version": "${current}"`, `"version": "${next}"`);
|
|
18504
|
+
if (updated === raw) {
|
|
18505
|
+
ui.error(`${rel} is not at ${current} \u2014 lockstep broken; fix versions manually first.`);
|
|
18506
|
+
process.exitCode = 1;
|
|
18507
|
+
return;
|
|
18508
|
+
}
|
|
18509
|
+
await writeFile38(file, updated, "utf8");
|
|
18510
|
+
}
|
|
18511
|
+
const changelog = path55.join(root, "CHANGELOG.md");
|
|
18512
|
+
if (existsSync77(changelog)) {
|
|
18513
|
+
const raw = await readFile25(changelog, "utf8");
|
|
18514
|
+
const heading = `## [${next}]${opts.title ? ` \u2014 ${opts.title}` : ""}`;
|
|
18515
|
+
if (!raw.includes(`## [${next}]`)) {
|
|
18516
|
+
await writeFile38(
|
|
18517
|
+
changelog,
|
|
18518
|
+
raw.replace("## [Unreleased]", `## [Unreleased]
|
|
18519
|
+
|
|
18520
|
+
${heading}
|
|
18521
|
+
|
|
18522
|
+
- TODO: describe the changes.
|
|
18523
|
+
`),
|
|
18524
|
+
"utf8"
|
|
18525
|
+
);
|
|
18526
|
+
}
|
|
18527
|
+
}
|
|
18528
|
+
ui.success(`Bumped ${current} \u2192 ${next} across ${VERSION_FILES2.length} manifest(s); CHANGELOG scaffolded.`);
|
|
18529
|
+
ui.info("Next: fill the CHANGELOG entry, build/test, commit (the gate runs), then `hivelore release tag`.");
|
|
18530
|
+
});
|
|
18531
|
+
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) => {
|
|
18532
|
+
const root = findProjectRoot53(opts.dir);
|
|
18533
|
+
const version = await readCurrentVersion(root);
|
|
18534
|
+
for (const rel of VERSION_FILES2.slice(1)) {
|
|
18535
|
+
const file = path55.join(root, rel);
|
|
18536
|
+
if (!existsSync77(file)) continue;
|
|
18537
|
+
const v = JSON.parse(await readFile25(file, "utf8")).version;
|
|
18538
|
+
if (v !== version) {
|
|
18539
|
+
ui.error(`${rel} is at ${v}, root at ${version} \u2014 lockstep broken; run \`hivelore release bump\` first.`);
|
|
18540
|
+
process.exitCode = 1;
|
|
18541
|
+
return;
|
|
18542
|
+
}
|
|
18543
|
+
}
|
|
18544
|
+
const dirty = (await exec2("git", ["status", "--porcelain"], { cwd: root })).stdout.trim();
|
|
18545
|
+
if (dirty.length > 0) {
|
|
18546
|
+
ui.error("Working tree is not clean \u2014 commit the bump before tagging.");
|
|
18547
|
+
process.exitCode = 1;
|
|
18548
|
+
return;
|
|
18549
|
+
}
|
|
18550
|
+
const tag = `v${version}`;
|
|
18551
|
+
const existing = (await exec2("git", ["tag", "--list", tag], { cwd: root })).stdout.trim();
|
|
18552
|
+
if (existing) {
|
|
18553
|
+
ui.error(`Tag ${tag} already exists \u2014 bump the version first.`);
|
|
18554
|
+
process.exitCode = 1;
|
|
18555
|
+
return;
|
|
18556
|
+
}
|
|
18557
|
+
await exec2("git", ["tag", tag], { cwd: root });
|
|
18558
|
+
ui.success(`Created ${tag} at HEAD.`);
|
|
18559
|
+
if (opts.push !== false) {
|
|
18560
|
+
await exec2("git", ["push"], { cwd: root });
|
|
18561
|
+
await exec2("git", ["push", "origin", tag], { cwd: root });
|
|
18562
|
+
ui.success(`Pushed branch and ${tag}.`);
|
|
18563
|
+
ui.info("Next: `hivelore enforce finish --wait` (polls CI), then publish via `pnpm run publish:all` (human step).");
|
|
18564
|
+
}
|
|
18565
|
+
});
|
|
18566
|
+
}
|
|
18567
|
+
|
|
18313
18568
|
// src/commands/run.ts
|
|
18314
18569
|
import "commander";
|
|
18315
18570
|
function registerRun(program2) {
|
|
@@ -18323,15 +18578,15 @@ function registerRun(program2) {
|
|
|
18323
18578
|
}
|
|
18324
18579
|
|
|
18325
18580
|
// src/commands/sensors.ts
|
|
18326
|
-
import { execFile as
|
|
18327
|
-
import { existsSync as
|
|
18328
|
-
import { chmod as chmod3, mkdir as
|
|
18329
|
-
import
|
|
18330
|
-
import { promisify as
|
|
18581
|
+
import { execFile as execFile5 } from "child_process";
|
|
18582
|
+
import { existsSync as existsSync78 } from "fs";
|
|
18583
|
+
import { chmod as chmod3, mkdir as mkdir21, readFile as readFile26, writeFile as writeFile39 } from "fs/promises";
|
|
18584
|
+
import path56 from "path";
|
|
18585
|
+
import { promisify as promisify5 } from "util";
|
|
18331
18586
|
import "commander";
|
|
18332
18587
|
import {
|
|
18333
18588
|
extractSensorExamples as extractSensorExamples2,
|
|
18334
|
-
findProjectRoot as
|
|
18589
|
+
findProjectRoot as findProjectRoot54,
|
|
18335
18590
|
isRetiredMemory as isRetiredMemory4,
|
|
18336
18591
|
judgeProposedSensor as judgeProposedSensor2,
|
|
18337
18592
|
loadConfig as loadConfig16,
|
|
@@ -18343,13 +18598,13 @@ import {
|
|
|
18343
18598
|
sensorPatternBrittleness as sensorPatternBrittleness2,
|
|
18344
18599
|
sensorSelfCheck as sensorSelfCheck2,
|
|
18345
18600
|
scannableSensorTargets,
|
|
18346
|
-
serializeMemory as
|
|
18601
|
+
serializeMemory as serializeMemory29
|
|
18347
18602
|
} from "@hivelore/core";
|
|
18348
|
-
var
|
|
18603
|
+
var exec3 = promisify5(execFile5);
|
|
18349
18604
|
function registerSensors(program2) {
|
|
18350
18605
|
const sensors = program2.command("sensors").description("Operate executable sensors derived from Hivelore memories");
|
|
18351
18606
|
sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
18352
|
-
const root =
|
|
18607
|
+
const root = findProjectRoot54(opts.dir);
|
|
18353
18608
|
const paths = resolveHaivePaths49(root);
|
|
18354
18609
|
const rows = await sensorRows(paths);
|
|
18355
18610
|
if (opts.json) {
|
|
@@ -18378,10 +18633,10 @@ function registerSensors(program2) {
|
|
|
18378
18633
|
sensors.command("check").description(
|
|
18379
18634
|
"Run regex sensors against a diff (the deterministic/computational layer); defaults to `git diff --cached`.\n Diff-scan layers: `sensors check` (regex) and `anti_patterns_check` (memory match) are components;\n `pre_commit_check` combines them; `hivelore enforce check` is THE gate that runs at commit."
|
|
18380
18635
|
).option("--diff-file <path>", "read unified diff from a file instead of staged changes").option("--json", "emit JSON", false).option("--commands", "ALSO execute shell/test sensors (runs repo-authored commands)", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
18381
|
-
const root =
|
|
18636
|
+
const root = findProjectRoot54(opts.dir);
|
|
18382
18637
|
const paths = resolveHaivePaths49(root);
|
|
18383
18638
|
const memories = await runnableSensorMemories(paths);
|
|
18384
|
-
const diff = opts.diffFile ? await
|
|
18639
|
+
const diff = opts.diffFile ? await readFile26(path56.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
|
|
18385
18640
|
const targets = scannableSensorTargets(diff);
|
|
18386
18641
|
const hits = runSensors3(memories, targets);
|
|
18387
18642
|
const config = await loadConfig16(paths);
|
|
@@ -18456,9 +18711,9 @@ function registerSensors(program2) {
|
|
|
18456
18711
|
process.exitCode = 1;
|
|
18457
18712
|
return;
|
|
18458
18713
|
}
|
|
18459
|
-
const root =
|
|
18714
|
+
const root = findProjectRoot54(opts.dir);
|
|
18460
18715
|
const paths = resolveHaivePaths49(root);
|
|
18461
|
-
const loaded =
|
|
18716
|
+
const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
|
|
18462
18717
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
18463
18718
|
if (!found) {
|
|
18464
18719
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -18508,7 +18763,7 @@ function registerSensors(program2) {
|
|
|
18508
18763
|
},
|
|
18509
18764
|
body: found.memory.body
|
|
18510
18765
|
};
|
|
18511
|
-
await writeFile39(found.filePath,
|
|
18766
|
+
await writeFile39(found.filePath, serializeMemory29(next), "utf8");
|
|
18512
18767
|
ui.success(`Updated ${id}: sensor severity=${severity}`);
|
|
18513
18768
|
if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
|
|
18514
18769
|
ui.info(`message=${sensor.message}`);
|
|
@@ -18525,9 +18780,9 @@ function registerSensors(program2) {
|
|
|
18525
18780
|
process.exitCode = 1;
|
|
18526
18781
|
return;
|
|
18527
18782
|
}
|
|
18528
|
-
const root =
|
|
18783
|
+
const root = findProjectRoot54(opts.dir);
|
|
18529
18784
|
const paths = resolveHaivePaths49(root);
|
|
18530
|
-
const loaded =
|
|
18785
|
+
const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
|
|
18531
18786
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
18532
18787
|
if (!found) {
|
|
18533
18788
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -18564,7 +18819,7 @@ function registerSensors(program2) {
|
|
|
18564
18819
|
process.exitCode = 1;
|
|
18565
18820
|
return;
|
|
18566
18821
|
}
|
|
18567
|
-
await writeFile39(found.filePath,
|
|
18822
|
+
await writeFile39(found.filePath, serializeMemory29({ frontmatter: { ...found.memory.frontmatter, sensor }, body: found.memory.body }), "utf8");
|
|
18568
18823
|
ui.success(`Sensor accepted (${severity}) on ${id}`);
|
|
18569
18824
|
ui.info(`pattern=${JSON.stringify(opts.pattern)}${opts.absent ? ` absent=${JSON.stringify(opts.absent)}` : ""}`);
|
|
18570
18825
|
ui.info(
|
|
@@ -18578,16 +18833,16 @@ function registerSensors(program2) {
|
|
|
18578
18833
|
process.exitCode = 1;
|
|
18579
18834
|
return;
|
|
18580
18835
|
}
|
|
18581
|
-
const root =
|
|
18836
|
+
const root = findProjectRoot54(opts.dir);
|
|
18582
18837
|
const paths = resolveHaivePaths49(root);
|
|
18583
18838
|
const rows = await sensorRows(paths);
|
|
18584
|
-
const outDir =
|
|
18585
|
-
await
|
|
18586
|
-
const outPath =
|
|
18839
|
+
const outDir = path56.resolve(root, opts.outDir ?? ".ai/generated");
|
|
18840
|
+
await mkdir21(outDir, { recursive: true });
|
|
18841
|
+
const outPath = path56.join(outDir, format === "grep" ? "haive-sensors-grep.sh" : "haive-sensors-eslint.json");
|
|
18587
18842
|
const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
|
|
18588
18843
|
await writeFile39(outPath, content, "utf8");
|
|
18589
18844
|
if (format === "grep") await chmod3(outPath, 493);
|
|
18590
|
-
ui.success(`Exported ${rows.length} sensor(s): ${
|
|
18845
|
+
ui.success(`Exported ${rows.length} sensor(s): ${path56.relative(root, outPath)}`);
|
|
18591
18846
|
});
|
|
18592
18847
|
}
|
|
18593
18848
|
function deriveProposedMessage(body, pattern, absent) {
|
|
@@ -18620,7 +18875,7 @@ async function sensorRows(paths) {
|
|
|
18620
18875
|
});
|
|
18621
18876
|
}
|
|
18622
18877
|
async function runnableSensorMemories(paths, regexOnly = true) {
|
|
18623
|
-
if (!
|
|
18878
|
+
if (!existsSync78(paths.memoriesDir)) return [];
|
|
18624
18879
|
const loaded = await loadMemoriesFromDir41(paths.memoriesDir);
|
|
18625
18880
|
return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
18626
18881
|
const sensor = memory2.frontmatter.sensor;
|
|
@@ -18631,7 +18886,7 @@ async function runnableSensorMemories(paths, regexOnly = true) {
|
|
|
18631
18886
|
}
|
|
18632
18887
|
async function runCommandSensor(spec, root) {
|
|
18633
18888
|
try {
|
|
18634
|
-
await
|
|
18889
|
+
await exec3("bash", ["-c", spec.command], { cwd: root, timeout: 12e4, maxBuffer: 8 * 1024 * 1024 });
|
|
18635
18890
|
return false;
|
|
18636
18891
|
} catch {
|
|
18637
18892
|
return true;
|
|
@@ -18639,7 +18894,7 @@ async function runCommandSensor(spec, root) {
|
|
|
18639
18894
|
}
|
|
18640
18895
|
async function stagedDiff(root) {
|
|
18641
18896
|
try {
|
|
18642
|
-
const { stdout } = await
|
|
18897
|
+
const { stdout } = await exec3("git", ["diff", "--cached"], { cwd: root });
|
|
18643
18898
|
return stdout;
|
|
18644
18899
|
} catch (err) {
|
|
18645
18900
|
throw new Error(`git diff --cached failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -18670,19 +18925,19 @@ function shellQuote(value) {
|
|
|
18670
18925
|
}
|
|
18671
18926
|
|
|
18672
18927
|
// src/commands/ingest.ts
|
|
18673
|
-
import { existsSync as
|
|
18674
|
-
import { mkdir as
|
|
18675
|
-
import
|
|
18928
|
+
import { existsSync as existsSync79 } from "fs";
|
|
18929
|
+
import { mkdir as mkdir23, readFile as readFile27, writeFile as writeFile40 } from "fs/promises";
|
|
18930
|
+
import path57 from "path";
|
|
18676
18931
|
import "commander";
|
|
18677
18932
|
import {
|
|
18678
18933
|
draftsFromFindings as draftsFromFindings2,
|
|
18679
18934
|
filterNewDrafts as filterNewDrafts2,
|
|
18680
|
-
findProjectRoot as
|
|
18935
|
+
findProjectRoot as findProjectRoot55,
|
|
18681
18936
|
loadMemoriesFromDir as loadMemoriesFromDir43,
|
|
18682
|
-
memoryFilePath as
|
|
18937
|
+
memoryFilePath as memoryFilePath11,
|
|
18683
18938
|
parseFindings as parseFindings2,
|
|
18684
18939
|
resolveHaivePaths as resolveHaivePaths50,
|
|
18685
|
-
serializeMemory as
|
|
18940
|
+
serializeMemory as serializeMemory30
|
|
18686
18941
|
} from "@hivelore/core";
|
|
18687
18942
|
var SEVERITIES = ["info", "minor", "major", "critical", "blocker"];
|
|
18688
18943
|
function registerIngest(program2) {
|
|
@@ -18706,9 +18961,9 @@ function registerIngest(program2) {
|
|
|
18706
18961
|
process.exitCode = 1;
|
|
18707
18962
|
return;
|
|
18708
18963
|
}
|
|
18709
|
-
const root =
|
|
18964
|
+
const root = findProjectRoot55(opts.dir);
|
|
18710
18965
|
const paths = resolveHaivePaths50(root);
|
|
18711
|
-
if (!
|
|
18966
|
+
if (!existsSync79(paths.haiveDir)) {
|
|
18712
18967
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
18713
18968
|
process.exitCode = 1;
|
|
18714
18969
|
return;
|
|
@@ -18729,14 +18984,14 @@ function registerIngest(program2) {
|
|
|
18729
18984
|
process.exitCode = 1;
|
|
18730
18985
|
return;
|
|
18731
18986
|
}
|
|
18732
|
-
const reportPath =
|
|
18733
|
-
if (!
|
|
18987
|
+
const reportPath = path57.resolve(root, file);
|
|
18988
|
+
if (!existsSync79(reportPath)) {
|
|
18734
18989
|
ui.error(`Report file not found: ${reportPath}`);
|
|
18735
18990
|
process.exitCode = 1;
|
|
18736
18991
|
return;
|
|
18737
18992
|
}
|
|
18738
18993
|
try {
|
|
18739
|
-
raw = await
|
|
18994
|
+
raw = await readFile27(reportPath, "utf8");
|
|
18740
18995
|
} catch (err) {
|
|
18741
18996
|
ui.error(`Could not read ${reportPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
18742
18997
|
process.exitCode = 1;
|
|
@@ -18762,7 +19017,7 @@ function registerIngest(program2) {
|
|
|
18762
19017
|
process.exitCode = 1;
|
|
18763
19018
|
return;
|
|
18764
19019
|
}
|
|
18765
|
-
const existing =
|
|
19020
|
+
const existing = existsSync79(paths.memoriesDir) ? await loadMemoriesFromDir43(paths.memoriesDir) : [];
|
|
18766
19021
|
const existingTopics = new Set(
|
|
18767
19022
|
existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
|
|
18768
19023
|
);
|
|
@@ -18824,14 +19079,14 @@ function registerIngest(program2) {
|
|
|
18824
19079
|
await writeDraft2(paths, draft);
|
|
18825
19080
|
created++;
|
|
18826
19081
|
}
|
|
18827
|
-
ui.success(`Created ${created} proposed memory(ies) under ${
|
|
19082
|
+
ui.success(`Created ${created} proposed memory(ies) under ${path57.relative(root, paths.memoriesDir)}/`);
|
|
18828
19083
|
ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
|
|
18829
19084
|
});
|
|
18830
19085
|
}
|
|
18831
19086
|
async function writeDraft2(paths, draft) {
|
|
18832
|
-
const file =
|
|
18833
|
-
await
|
|
18834
|
-
await writeFile40(file,
|
|
19087
|
+
const file = memoryFilePath11(paths, draft.frontmatter.scope, draft.frontmatter.id, draft.frontmatter.module);
|
|
19088
|
+
await mkdir23(path57.dirname(file), { recursive: true });
|
|
19089
|
+
await writeFile40(file, serializeMemory30({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
18835
19090
|
return file;
|
|
18836
19091
|
}
|
|
18837
19092
|
async function fetchSonarIssues(opts) {
|
|
@@ -18872,11 +19127,11 @@ async function fetchSonarIssues(opts) {
|
|
|
18872
19127
|
}
|
|
18873
19128
|
|
|
18874
19129
|
// src/commands/dashboard.ts
|
|
18875
|
-
import { existsSync as
|
|
19130
|
+
import { existsSync as existsSync80 } from "fs";
|
|
18876
19131
|
import "commander";
|
|
18877
19132
|
import {
|
|
18878
19133
|
buildDashboard,
|
|
18879
|
-
findProjectRoot as
|
|
19134
|
+
findProjectRoot as findProjectRoot56,
|
|
18880
19135
|
loadConfig as loadConfig17,
|
|
18881
19136
|
loadMemoriesFromDir as loadMemoriesFromDir44,
|
|
18882
19137
|
loadPreventionEvents as loadPreventionEvents4,
|
|
@@ -18887,14 +19142,14 @@ function registerDashboard(program2) {
|
|
|
18887
19142
|
program2.command("dashboard").description(
|
|
18888
19143
|
"Non-interactive observability snapshot of the memory corpus.\n\n One-shot rollup an agent or CI can read (unlike `hivelore tui`, no TTY needed):\n inventory, impact tiers + top memories, sensors (and which ones fired),\n health (stale / anchorless / pending / prune candidates), decay, and corpus weight.\n Use --json to pipe it into other tooling."
|
|
18889
19144
|
).option("--json", "emit the full report as JSON", false).option("--top <n>", "rows per top-list", "10").option("--dormant-days <n>", "dormancy window for impact scoring").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
18890
|
-
const root =
|
|
19145
|
+
const root = findProjectRoot56(opts.dir);
|
|
18891
19146
|
const paths = resolveHaivePaths51(root);
|
|
18892
|
-
if (!
|
|
19147
|
+
if (!existsSync80(paths.haiveDir)) {
|
|
18893
19148
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
18894
19149
|
process.exitCode = 1;
|
|
18895
19150
|
return;
|
|
18896
19151
|
}
|
|
18897
|
-
const memories =
|
|
19152
|
+
const memories = existsSync80(paths.memoriesDir) ? await loadMemoriesFromDir44(paths.memoriesDir) : [];
|
|
18898
19153
|
const usage = await loadUsageIndex31(paths);
|
|
18899
19154
|
const preventionEvents = await loadPreventionEvents4(paths);
|
|
18900
19155
|
const config = await loadConfig17(paths);
|
|
@@ -19018,30 +19273,30 @@ function warnNum(n) {
|
|
|
19018
19273
|
}
|
|
19019
19274
|
|
|
19020
19275
|
// src/commands/dev-link.ts
|
|
19021
|
-
import { execFile as
|
|
19022
|
-
import { cp, readFile as
|
|
19023
|
-
import { existsSync as
|
|
19024
|
-
import
|
|
19025
|
-
import { promisify as
|
|
19276
|
+
import { execFile as execFile6 } from "child_process";
|
|
19277
|
+
import { cp, readFile as readFile28 } from "fs/promises";
|
|
19278
|
+
import { existsSync as existsSync81 } from "fs";
|
|
19279
|
+
import path58 from "path";
|
|
19280
|
+
import { promisify as promisify6 } from "util";
|
|
19026
19281
|
import "commander";
|
|
19027
|
-
import { findProjectRoot as
|
|
19028
|
-
var
|
|
19282
|
+
import { findProjectRoot as findProjectRoot57 } from "@hivelore/core";
|
|
19283
|
+
var exec4 = promisify6(execFile6);
|
|
19029
19284
|
function registerDevLink(program2) {
|
|
19030
19285
|
const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
|
|
19031
19286
|
dev.command("link").description("Hot-swap this repo's built dist into the global @hivelore (or legacy @hiveai) 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) => {
|
|
19032
|
-
const root =
|
|
19033
|
-
if (!
|
|
19287
|
+
const root = findProjectRoot57(opts.dir);
|
|
19288
|
+
if (!existsSync81(path58.join(root, "packages", "cli", "dist", "index.js"))) {
|
|
19034
19289
|
ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
|
|
19035
19290
|
process.exitCode = 1;
|
|
19036
19291
|
return;
|
|
19037
19292
|
}
|
|
19038
19293
|
let globalModules;
|
|
19039
19294
|
try {
|
|
19040
|
-
globalModules = (await
|
|
19295
|
+
globalModules = (await exec4("npm", ["root", "-g"])).stdout.trim();
|
|
19041
19296
|
} catch {
|
|
19042
|
-
globalModules =
|
|
19297
|
+
globalModules = path58.join(path58.dirname(path58.dirname(process.execPath)), "lib", "node_modules");
|
|
19043
19298
|
}
|
|
19044
|
-
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) =>
|
|
19299
|
+
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path58.join(globalModules, scope)).filter((dir) => existsSync81(dir));
|
|
19045
19300
|
if (scopeDirs.length === 0) {
|
|
19046
19301
|
ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
|
|
19047
19302
|
process.exitCode = 1;
|
|
@@ -19049,24 +19304,24 @@ function registerDevLink(program2) {
|
|
|
19049
19304
|
}
|
|
19050
19305
|
const linked = [];
|
|
19051
19306
|
const copyDist = async (fromPkg, toDistDir) => {
|
|
19052
|
-
const from =
|
|
19053
|
-
if (!
|
|
19307
|
+
const from = path58.join(root, "packages", fromPkg, "dist");
|
|
19308
|
+
if (!existsSync81(from) || !existsSync81(path58.dirname(toDistDir))) return;
|
|
19054
19309
|
await cp(from, toDistDir, { recursive: true });
|
|
19055
|
-
linked.push(
|
|
19310
|
+
linked.push(path58.relative(globalModules, toDistDir));
|
|
19056
19311
|
};
|
|
19057
19312
|
for (const globalHive of scopeDirs) {
|
|
19058
|
-
const nestedScope =
|
|
19313
|
+
const nestedScope = path58.basename(globalHive);
|
|
19059
19314
|
for (const pkg of ["cli", "mcp"]) {
|
|
19060
|
-
await copyDist(pkg,
|
|
19315
|
+
await copyDist(pkg, path58.join(globalHive, pkg, "dist"));
|
|
19061
19316
|
for (const nested of ["core", "embeddings"]) {
|
|
19062
|
-
await copyDist(nested,
|
|
19317
|
+
await copyDist(nested, path58.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
|
|
19063
19318
|
}
|
|
19064
19319
|
}
|
|
19065
|
-
await copyDist("core",
|
|
19320
|
+
await copyDist("core", path58.join(globalHive, "core", "dist"));
|
|
19066
19321
|
}
|
|
19067
19322
|
let version = "unknown";
|
|
19068
19323
|
try {
|
|
19069
|
-
version = JSON.parse(await
|
|
19324
|
+
version = JSON.parse(await readFile28(path58.join(root, "package.json"), "utf8")).version ?? "unknown";
|
|
19070
19325
|
} catch {
|
|
19071
19326
|
}
|
|
19072
19327
|
if (opts.json) {
|
|
@@ -19077,27 +19332,27 @@ function registerDevLink(program2) {
|
|
|
19077
19332
|
ui.warn("Nothing linked \u2014 no matching dist targets were found in the global install.");
|
|
19078
19333
|
return;
|
|
19079
19334
|
}
|
|
19080
|
-
ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) =>
|
|
19335
|
+
ui.success(`Linked local dist (v${version}) into the global install(s): ${scopeDirs.map((d) => path58.basename(d)).join(", ")}`);
|
|
19081
19336
|
for (const t of linked) console.log(` ${ui.dim("\u2192")} ${t}`);
|
|
19082
19337
|
console.log(ui.dim("The global binary now runs your local build (git hooks + MCP included)."));
|
|
19083
19338
|
});
|
|
19084
19339
|
}
|
|
19085
19340
|
|
|
19086
19341
|
// src/commands/coverage.ts
|
|
19087
|
-
import { readFile as
|
|
19088
|
-
import { existsSync as
|
|
19089
|
-
import
|
|
19342
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
19343
|
+
import { existsSync as existsSync83 } from "fs";
|
|
19344
|
+
import path59 from "path";
|
|
19090
19345
|
import "commander";
|
|
19091
19346
|
import {
|
|
19092
19347
|
findCoverageGaps,
|
|
19093
|
-
findProjectRoot as
|
|
19348
|
+
findProjectRoot as findProjectRoot58,
|
|
19094
19349
|
mergeHotFiles,
|
|
19095
19350
|
resolveHaivePaths as resolveHaivePaths52,
|
|
19096
19351
|
tallyHotFiles
|
|
19097
19352
|
} from "@hivelore/core";
|
|
19098
19353
|
async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
19099
|
-
if (!
|
|
19100
|
-
const raw = await
|
|
19354
|
+
if (!existsSync83(cacheFile)) return [];
|
|
19355
|
+
const raw = await readFile29(cacheFile, "utf8").catch(() => "");
|
|
19101
19356
|
const files = [];
|
|
19102
19357
|
for (const line of raw.split("\n")) {
|
|
19103
19358
|
const trimmed = line.trim();
|
|
@@ -19110,7 +19365,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
|
19110
19365
|
}
|
|
19111
19366
|
for (const f of obs.files ?? []) {
|
|
19112
19367
|
if (typeof f !== "string" || !f) continue;
|
|
19113
|
-
const rel =
|
|
19368
|
+
const rel = path59.isAbsolute(f) ? path59.relative(root, f) : f;
|
|
19114
19369
|
if (rel.startsWith("..")) continue;
|
|
19115
19370
|
files.push(rel);
|
|
19116
19371
|
}
|
|
@@ -19131,7 +19386,7 @@ function registerCoverage(program2) {
|
|
|
19131
19386
|
program2.command("coverage").description(
|
|
19132
19387
|
"Coverage-gap report: frequently-edited files with no covering team memory (blind spots)."
|
|
19133
19388
|
).option("--json", "emit JSON", false).option("--min-changes <n>", "minimum churn count to flag a file", "3").option("--limit <n>", "max gaps to report", "20").option("--days <n>", "lookback window in days (git history + agent edits)", "90").option("--source <which>", "heat source: git | agent | both", "both").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
19134
|
-
const root =
|
|
19389
|
+
const root = findProjectRoot58(opts.dir);
|
|
19135
19390
|
const paths = resolveHaivePaths52(root);
|
|
19136
19391
|
const minChanges = Math.max(1, parseInt(opts.minChanges ?? "3", 10) || 3);
|
|
19137
19392
|
const limit = Math.max(1, parseInt(opts.limit ?? "20", 10) || 20);
|
|
@@ -19154,7 +19409,7 @@ function registerCoverage(program2) {
|
|
|
19154
19409
|
}) : null;
|
|
19155
19410
|
const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
|
|
19156
19411
|
const sinceMs = Date.now() - days * 864e5;
|
|
19157
|
-
const agentHotFiles = useAgent ? (await readAgentHotFiles(root,
|
|
19412
|
+
const agentHotFiles = useAgent ? (await readAgentHotFiles(root, path59.join(paths.haiveDir, ".cache", "observations.jsonl"), sinceMs)).filter((h) => !isNoisePath(h.path)) : [];
|
|
19158
19413
|
const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
|
|
19159
19414
|
const memories = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
19160
19415
|
const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
|
|
@@ -19192,10 +19447,10 @@ function registerCoverage(program2) {
|
|
|
19192
19447
|
|
|
19193
19448
|
// src/commands/merge-driver.ts
|
|
19194
19449
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
19195
|
-
import { readFileSync, writeFileSync, existsSync as
|
|
19196
|
-
import
|
|
19450
|
+
import { readFileSync, writeFileSync, existsSync as existsSync84 } from "fs";
|
|
19451
|
+
import path60 from "path";
|
|
19197
19452
|
import "commander";
|
|
19198
|
-
import { findProjectRoot as
|
|
19453
|
+
import { findProjectRoot as findProjectRoot59, mergeMemoryVersions } from "@hivelore/core";
|
|
19199
19454
|
var GITATTRIBUTES_MARK = "# Hivelore merge driver";
|
|
19200
19455
|
var GITATTRIBUTES_BLOCK = [
|
|
19201
19456
|
GITATTRIBUTES_MARK,
|
|
@@ -19216,7 +19471,7 @@ function registerMergeDriver(program2) {
|
|
|
19216
19471
|
}
|
|
19217
19472
|
});
|
|
19218
19473
|
cmd.command("install").description("Configure git + .gitattributes so memory-file conflicts auto-resolve").option("-d, --dir <dir>", "project root").action((opts) => {
|
|
19219
|
-
const root =
|
|
19474
|
+
const root = findProjectRoot59(opts.dir);
|
|
19220
19475
|
try {
|
|
19221
19476
|
execFileSync3("git", ["config", "merge.haive.name", "Hivelore memory merge driver"], { cwd: root });
|
|
19222
19477
|
execFileSync3("git", ["config", "merge.haive.driver", "hivelore merge-driver run %O %A %B"], { cwd: root });
|
|
@@ -19225,8 +19480,8 @@ function registerMergeDriver(program2) {
|
|
|
19225
19480
|
process.exitCode = 1;
|
|
19226
19481
|
return;
|
|
19227
19482
|
}
|
|
19228
|
-
const gaPath =
|
|
19229
|
-
let content =
|
|
19483
|
+
const gaPath = path60.join(root, ".gitattributes");
|
|
19484
|
+
let content = existsSync84(gaPath) ? readFileSync(gaPath, "utf8") : "";
|
|
19230
19485
|
if (!content.includes(GITATTRIBUTES_MARK)) {
|
|
19231
19486
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
19232
19487
|
content += GITATTRIBUTES_BLOCK + "\n";
|
|
@@ -19241,20 +19496,20 @@ function registerMergeDriver(program2) {
|
|
|
19241
19496
|
|
|
19242
19497
|
// src/commands/memory-resolve-conflict.ts
|
|
19243
19498
|
import { writeFile as writeFile41 } from "fs/promises";
|
|
19244
|
-
import { existsSync as
|
|
19499
|
+
import { existsSync as existsSync85 } from "fs";
|
|
19245
19500
|
import "commander";
|
|
19246
19501
|
import {
|
|
19247
19502
|
applyConflictResolution,
|
|
19248
|
-
findProjectRoot as
|
|
19503
|
+
findProjectRoot as findProjectRoot60,
|
|
19249
19504
|
planConflictResolution as planConflictResolution2,
|
|
19250
19505
|
resolveHaivePaths as resolveHaivePaths53,
|
|
19251
|
-
serializeMemory as
|
|
19506
|
+
serializeMemory as serializeMemory31
|
|
19252
19507
|
} from "@hivelore/core";
|
|
19253
19508
|
function registerMemoryResolveConflict(memory2) {
|
|
19254
19509
|
memory2.command("resolve-conflict <id_a> <id_b>").description("Resolve a contradiction: keep the stronger memory, deprecate (supersede) the other").option("--yes", "apply the resolution (without this, only previews it)", false).option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (idA, idB, opts) => {
|
|
19255
|
-
const root =
|
|
19510
|
+
const root = findProjectRoot60(opts.dir);
|
|
19256
19511
|
const paths = resolveHaivePaths53(root);
|
|
19257
|
-
if (!
|
|
19512
|
+
if (!existsSync85(paths.memoriesDir)) {
|
|
19258
19513
|
ui.error(`No .ai/memories at ${root}.`);
|
|
19259
19514
|
process.exitCode = 1;
|
|
19260
19515
|
return;
|
|
@@ -19294,12 +19549,12 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
19294
19549
|
}
|
|
19295
19550
|
await writeFile41(
|
|
19296
19551
|
winner.filePath,
|
|
19297
|
-
|
|
19552
|
+
serializeMemory31({ frontmatter: applied.winner, body: winner.memory.body }),
|
|
19298
19553
|
"utf8"
|
|
19299
19554
|
);
|
|
19300
19555
|
await writeFile41(
|
|
19301
19556
|
loser.filePath,
|
|
19302
|
-
|
|
19557
|
+
serializeMemory31({ frontmatter: applied.loser, body: loser.memory.body }),
|
|
19303
19558
|
"utf8"
|
|
19304
19559
|
);
|
|
19305
19560
|
if (!opts.json) {
|
|
@@ -19309,26 +19564,26 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
19309
19564
|
}
|
|
19310
19565
|
|
|
19311
19566
|
// src/commands/memory-seed-git.ts
|
|
19312
|
-
import { execFile as
|
|
19313
|
-
import { mkdir as
|
|
19314
|
-
import { existsSync as
|
|
19315
|
-
import
|
|
19316
|
-
import { promisify as
|
|
19567
|
+
import { execFile as execFile7 } from "child_process";
|
|
19568
|
+
import { mkdir as mkdir24, writeFile as writeFile43 } from "fs/promises";
|
|
19569
|
+
import { existsSync as existsSync86 } from "fs";
|
|
19570
|
+
import path61 from "path";
|
|
19571
|
+
import { promisify as promisify7 } from "util";
|
|
19317
19572
|
import "commander";
|
|
19318
19573
|
import {
|
|
19319
|
-
buildFrontmatter as
|
|
19320
|
-
findProjectRoot as
|
|
19321
|
-
memoryFilePath as
|
|
19574
|
+
buildFrontmatter as buildFrontmatter11,
|
|
19575
|
+
findProjectRoot as findProjectRoot61,
|
|
19576
|
+
memoryFilePath as memoryFilePath12,
|
|
19322
19577
|
proposeSeedsFromCommits as proposeSeedsFromCommits2,
|
|
19323
19578
|
resolveHaivePaths as resolveHaivePaths54,
|
|
19324
|
-
serializeMemory as
|
|
19579
|
+
serializeMemory as serializeMemory33
|
|
19325
19580
|
} from "@hivelore/core";
|
|
19326
|
-
var
|
|
19581
|
+
var exec5 = promisify7(execFile7);
|
|
19327
19582
|
function registerMemorySeedGit(memory2) {
|
|
19328
19583
|
memory2.command("seed-git").description("Propose draft `attempt` seeds from revert/hotfix commits in git history (cold-start)").option("--apply", "write the proposed seeds as draft memories (default: preview only)", false).option("--limit <n>", "max seeds to propose", "20").option("--days <n>", "git-history lookback window in days", "365").option("--scope <scope>", "personal | team", "team").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
19329
|
-
const root =
|
|
19584
|
+
const root = findProjectRoot61(opts.dir);
|
|
19330
19585
|
const paths = resolveHaivePaths54(root);
|
|
19331
|
-
if (!
|
|
19586
|
+
if (!existsSync86(paths.haiveDir)) {
|
|
19332
19587
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
19333
19588
|
process.exitCode = 1;
|
|
19334
19589
|
return;
|
|
@@ -19356,7 +19611,7 @@ function registerMemorySeedGit(memory2) {
|
|
|
19356
19611
|
let written = 0;
|
|
19357
19612
|
for (const p of proposals) {
|
|
19358
19613
|
const fm = {
|
|
19359
|
-
...
|
|
19614
|
+
...buildFrontmatter11({
|
|
19360
19615
|
type: "attempt",
|
|
19361
19616
|
slug: p.slug,
|
|
19362
19617
|
scope: opts.scope ?? "team",
|
|
@@ -19372,10 +19627,10 @@ function registerMemorySeedGit(memory2) {
|
|
|
19372
19627
|
|
|
19373
19628
|
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
19374
19629
|
`;
|
|
19375
|
-
const file =
|
|
19376
|
-
if (
|
|
19377
|
-
await
|
|
19378
|
-
await writeFile43(file,
|
|
19630
|
+
const file = memoryFilePath12(paths, fm.scope, fm.id, fm.module);
|
|
19631
|
+
if (existsSync86(file)) continue;
|
|
19632
|
+
await mkdir24(path61.dirname(file), { recursive: true });
|
|
19633
|
+
await writeFile43(file, serializeMemory33({ frontmatter: fm, body }), "utf8");
|
|
19379
19634
|
written += 1;
|
|
19380
19635
|
}
|
|
19381
19636
|
if (!opts.json) {
|
|
@@ -19385,7 +19640,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
|
|
|
19385
19640
|
}
|
|
19386
19641
|
async function readCommits(root, days) {
|
|
19387
19642
|
try {
|
|
19388
|
-
const { stdout } = await
|
|
19643
|
+
const { stdout } = await exec5(
|
|
19389
19644
|
"git",
|
|
19390
19645
|
["log", `--since=${days}.days.ago`, "--name-only", "--pretty=format:%x1f%h%x1f%s", "-n", "500"],
|
|
19391
19646
|
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
@@ -19406,11 +19661,11 @@ async function readCommits(root, days) {
|
|
|
19406
19661
|
}
|
|
19407
19662
|
|
|
19408
19663
|
// src/commands/bridges.ts
|
|
19409
|
-
import { existsSync as
|
|
19410
|
-
import
|
|
19664
|
+
import { existsSync as existsSync87 } from "fs";
|
|
19665
|
+
import path63 from "path";
|
|
19411
19666
|
import "commander";
|
|
19412
19667
|
import {
|
|
19413
|
-
findProjectRoot as
|
|
19668
|
+
findProjectRoot as findProjectRoot62,
|
|
19414
19669
|
resolveHaivePaths as resolveHaivePaths55,
|
|
19415
19670
|
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
|
|
19416
19671
|
BRIDGE_TARGETS as BRIDGE_TARGETS3
|
|
@@ -19425,10 +19680,10 @@ function registerBridges(program2) {
|
|
|
19425
19680
|
"--only <targets>",
|
|
19426
19681
|
"comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
|
|
19427
19682
|
).option("--max-memories <n>", "max memories to inject per bridge", "8").option("--dry-run", "show what would change without writing").option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
19428
|
-
const root =
|
|
19683
|
+
const root = findProjectRoot62(opts.dir);
|
|
19429
19684
|
const paths = resolveHaivePaths55(root);
|
|
19430
19685
|
const dryRun = opts.dryRun === true;
|
|
19431
|
-
if (!
|
|
19686
|
+
if (!existsSync87(paths.memoriesDir)) {
|
|
19432
19687
|
ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
19433
19688
|
process.exitCode = 1;
|
|
19434
19689
|
return;
|
|
@@ -19447,7 +19702,7 @@ function registerBridges(program2) {
|
|
|
19447
19702
|
targets = BRIDGE_TARGETS3;
|
|
19448
19703
|
} else {
|
|
19449
19704
|
targets = BRIDGE_TARGETS3.filter(
|
|
19450
|
-
(t) =>
|
|
19705
|
+
(t) => existsSync87(path63.join(root, BRIDGE_TARGET_PATH3[t]))
|
|
19451
19706
|
);
|
|
19452
19707
|
if (targets.length === 0) {
|
|
19453
19708
|
ui.info(
|
|
@@ -19476,7 +19731,7 @@ function registerBridges(program2) {
|
|
|
19476
19731
|
console.log(ui.dim(`bridges: ${parts.join(" \xB7 ") || "nothing to do"}`));
|
|
19477
19732
|
});
|
|
19478
19733
|
bridges.command("status").alias("list").description("List bridge targets and whether their Hivelore-managed blocks are current").option("-d, --dir <dir>", "project root").option("--max-memories <n>", "max memories expected in generated bridge blocks", "8").action(async (opts) => {
|
|
19479
|
-
const root =
|
|
19734
|
+
const root = findProjectRoot62(opts.dir);
|
|
19480
19735
|
const paths = resolveHaivePaths55(root);
|
|
19481
19736
|
const statuses = await getBridgeFileStatuses(root, paths, {
|
|
19482
19737
|
targets: BRIDGE_TARGETS3,
|
|
@@ -19494,13 +19749,14 @@ function registerBridges(program2) {
|
|
|
19494
19749
|
}
|
|
19495
19750
|
|
|
19496
19751
|
// src/index.ts
|
|
19497
|
-
var program = new
|
|
19498
|
-
program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.
|
|
19752
|
+
var program = new Command65();
|
|
19753
|
+
program.name("hivelore").description("Hivelore - repo-native memory and context policy for coding-agent harnesses").version("0.31.0").option("--advanced", "show maintenance and experimental commands in help").showSuggestionAfterError(true);
|
|
19499
19754
|
registerInit(program);
|
|
19500
19755
|
registerWelcome(program);
|
|
19501
19756
|
registerResolveProject(program);
|
|
19502
19757
|
registerRuntime(program);
|
|
19503
19758
|
registerEnforce(program);
|
|
19759
|
+
registerRelease(program);
|
|
19504
19760
|
registerRun(program);
|
|
19505
19761
|
registerAgent(program);
|
|
19506
19762
|
registerSensors(program);
|