@hivelore/cli 0.30.1 → 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 +588 -355
- 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";
|
|
@@ -16539,7 +16627,7 @@ function registerEnforce(program2) {
|
|
|
16539
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) => {
|
|
16540
16628
|
const root = findProjectRoot52(opts.dir);
|
|
16541
16629
|
const paths = resolveHaivePaths48(root);
|
|
16542
|
-
await
|
|
16630
|
+
await mkdir20(paths.haiveDir, { recursive: true });
|
|
16543
16631
|
const current = await loadConfig15(paths);
|
|
16544
16632
|
await saveConfig4(paths, {
|
|
16545
16633
|
...current,
|
|
@@ -16607,8 +16695,18 @@ function registerEnforce(program2) {
|
|
|
16607
16695
|
});
|
|
16608
16696
|
enforce.command("finish").alias("completion").description(
|
|
16609
16697
|
"Final agent-exit gate: verify the git sync/release protocol before reporting a task done."
|
|
16610
|
-
).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) => {
|
|
16611
|
-
|
|
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
|
+
}
|
|
16612
16710
|
printReport(report, Boolean(opts.json), Boolean(opts.explain));
|
|
16613
16711
|
if (report.should_block) {
|
|
16614
16712
|
if (!opts.json) printNextRequiredAction(report);
|
|
@@ -16631,7 +16729,7 @@ function registerEnforce(program2) {
|
|
|
16631
16729
|
if (!root) return;
|
|
16632
16730
|
const paths = resolveHaivePaths48(root);
|
|
16633
16731
|
if (!existsSync76(paths.haiveDir)) return;
|
|
16634
|
-
await
|
|
16732
|
+
await mkdir20(paths.runtimeDir, { recursive: true });
|
|
16635
16733
|
const sessionId = opts.sessionId ?? payload.session_id;
|
|
16636
16734
|
const task = opts.task ?? payload.prompt ?? "Start an AI coding session in this Hivelore-initialized project.";
|
|
16637
16735
|
await applyLightweightRepairs(root, paths);
|
|
@@ -17075,7 +17173,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
17075
17173
|
memoryIds: briefing.memories.map((m) => m.id)
|
|
17076
17174
|
});
|
|
17077
17175
|
const dir = path54.join(paths.runtimeDir, "enforcement", "briefings");
|
|
17078
|
-
await
|
|
17176
|
+
await mkdir20(dir, { recursive: true });
|
|
17079
17177
|
const file = path54.join(dir, `${sessionId}.md`);
|
|
17080
17178
|
const parts = [
|
|
17081
17179
|
"# Hivelore Briefing",
|
|
@@ -17094,7 +17192,7 @@ async function writeWrapperBriefing(paths, sessionId, task) {
|
|
|
17094
17192
|
if (briefing.setup_warnings.length > 0) {
|
|
17095
17193
|
parts.push("", "## Setup Warnings", ...briefing.setup_warnings.map((w) => `- ${w}`));
|
|
17096
17194
|
}
|
|
17097
|
-
await
|
|
17195
|
+
await writeFile37(file, parts.join("\n") + "\n", "utf8");
|
|
17098
17196
|
return file;
|
|
17099
17197
|
}
|
|
17100
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;
|
|
@@ -17180,7 +17278,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
17180
17278
|
findings: [{ severity: "info", code: "enforcement-off", message: "Hivelore enforcement is disabled." }]
|
|
17181
17279
|
});
|
|
17182
17280
|
}
|
|
17183
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
17281
|
+
findings.push(...await inspectIntegrationVersions(root, "0.31.0"));
|
|
17184
17282
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
17185
17283
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
17186
17284
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent Hivelore briefing marker exists." } : {
|
|
@@ -17428,14 +17526,39 @@ async function runPrecommitPolicy(paths, gate, stage) {
|
|
|
17428
17526
|
const reviewWarnings = result.warnings.filter(
|
|
17429
17527
|
(w) => w.level === "review" && !w.reasons.includes("sensor")
|
|
17430
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;
|
|
17431
17543
|
const reviewFinding = reviewWarnings.length > 0 ? [{
|
|
17432
17544
|
severity: "warn",
|
|
17433
17545
|
code: "anti-pattern-review",
|
|
17434
|
-
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)` : "") + ".",
|
|
17435
17547
|
fix: "Run `hivelore precommit` for the matched lines and repair commands; update the code, or retire the memory if it no longer applies.",
|
|
17436
17548
|
memory_ids: reviewWarnings.slice(0, 10).map((w) => w.id),
|
|
17437
17549
|
impact: 5
|
|
17438
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
|
+
}
|
|
17439
17562
|
if (!result.should_block) {
|
|
17440
17563
|
return [
|
|
17441
17564
|
{
|
|
@@ -17568,7 +17691,7 @@ function isGeneratedArtifactStatusLine(line) {
|
|
|
17568
17691
|
}
|
|
17569
17692
|
async function cleanupRuntimeDir(runtimeDir) {
|
|
17570
17693
|
let removed = 0;
|
|
17571
|
-
await
|
|
17694
|
+
await mkdir20(runtimeDir, { recursive: true });
|
|
17572
17695
|
const entries = await readdir8(runtimeDir, { withFileTypes: true }).catch(() => []);
|
|
17573
17696
|
for (const entry of entries) {
|
|
17574
17697
|
if (entry.name === ".gitignore" || entry.name === "README.md") continue;
|
|
@@ -17579,9 +17702,9 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
17579
17702
|
await rm3(path54.join(runtimeDir, entry.name), { recursive: true, force: true });
|
|
17580
17703
|
removed++;
|
|
17581
17704
|
}
|
|
17582
|
-
await
|
|
17705
|
+
await writeFile37(path54.join(runtimeDir, ".gitignore"), "*\n!.gitignore\n!README.md\n", "utf8");
|
|
17583
17706
|
if (!existsSync76(path54.join(runtimeDir, "README.md"))) {
|
|
17584
|
-
await
|
|
17707
|
+
await writeFile37(
|
|
17585
17708
|
path54.join(runtimeDir, "README.md"),
|
|
17586
17709
|
"# .ai/.runtime \u2014 disposable local layer\n\nRuntime data is local. Hivelore cleanup preserves briefing markers so enforcement state remains valid.\n",
|
|
17587
17710
|
"utf8"
|
|
@@ -17591,14 +17714,14 @@ async function cleanupRuntimeDir(runtimeDir) {
|
|
|
17591
17714
|
}
|
|
17592
17715
|
async function cleanupCacheDir(cacheDir) {
|
|
17593
17716
|
let removed = 0;
|
|
17594
|
-
await
|
|
17717
|
+
await mkdir20(cacheDir, { recursive: true });
|
|
17595
17718
|
const entries = await readdir8(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
17596
17719
|
for (const entry of entries) {
|
|
17597
17720
|
if (entry.name === ".gitignore") continue;
|
|
17598
17721
|
await rm3(path54.join(cacheDir, entry.name), { recursive: true, force: true });
|
|
17599
17722
|
removed++;
|
|
17600
17723
|
}
|
|
17601
|
-
await
|
|
17724
|
+
await writeFile37(path54.join(cacheDir, ".gitignore"), "*\n!.gitignore\n", "utf8");
|
|
17602
17725
|
return removed;
|
|
17603
17726
|
}
|
|
17604
17727
|
async function cleanupEnforcementDir(enforcementDir) {
|
|
@@ -18069,7 +18192,7 @@ async function installGitEnforcement(root) {
|
|
|
18069
18192
|
ui.warn("No .git directory found; git enforcement hooks skipped.");
|
|
18070
18193
|
return;
|
|
18071
18194
|
}
|
|
18072
|
-
await
|
|
18195
|
+
await mkdir20(hooksDir, { recursive: true });
|
|
18073
18196
|
const resolveCli = `_hivelore() {
|
|
18074
18197
|
if command -v hivelore >/dev/null 2>&1; then hivelore "$@"
|
|
18075
18198
|
elif command -v haive >/dev/null 2>&1; then haive "$@"
|
|
@@ -18107,14 +18230,14 @@ _hivelore enforce commit-msg "$1" --dir . || exit $?
|
|
|
18107
18230
|
if (existsSync76(file)) {
|
|
18108
18231
|
const current = await readFile24(file, "utf8").catch(() => "");
|
|
18109
18232
|
if (current.includes(ENFORCE_HOOK_MARKER)) {
|
|
18110
|
-
await
|
|
18233
|
+
await writeFile37(file, hook.body, "utf8");
|
|
18111
18234
|
} else {
|
|
18112
|
-
await
|
|
18235
|
+
await writeFile37(file, `${current.trimEnd()}
|
|
18113
18236
|
|
|
18114
18237
|
${hook.body}`, "utf8");
|
|
18115
18238
|
}
|
|
18116
18239
|
} else {
|
|
18117
|
-
await
|
|
18240
|
+
await writeFile37(file, hook.body, "utf8");
|
|
18118
18241
|
}
|
|
18119
18242
|
await chmod2(file, 493);
|
|
18120
18243
|
}
|
|
@@ -18122,12 +18245,12 @@ ${hook.body}`, "utf8");
|
|
|
18122
18245
|
}
|
|
18123
18246
|
async function installCiEnforcement(root) {
|
|
18124
18247
|
const workflowPath = path54.join(root, ".github", "workflows", "haive-enforcement.yml");
|
|
18125
|
-
await
|
|
18248
|
+
await mkdir20(path54.dirname(workflowPath), { recursive: true });
|
|
18126
18249
|
if (existsSync76(workflowPath)) {
|
|
18127
18250
|
ui.info("GitHub Actions enforcement workflow already exists \u2014 skipped");
|
|
18128
18251
|
return;
|
|
18129
18252
|
}
|
|
18130
|
-
await
|
|
18253
|
+
await writeFile37(workflowPath, `name: haive-enforcement
|
|
18131
18254
|
|
|
18132
18255
|
on:
|
|
18133
18256
|
pull_request:
|
|
@@ -18333,6 +18456,115 @@ function runCommand4(cmd, args, cwd) {
|
|
|
18333
18456
|
});
|
|
18334
18457
|
}
|
|
18335
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
|
+
|
|
18336
18568
|
// src/commands/run.ts
|
|
18337
18569
|
import "commander";
|
|
18338
18570
|
function registerRun(program2) {
|
|
@@ -18346,15 +18578,15 @@ function registerRun(program2) {
|
|
|
18346
18578
|
}
|
|
18347
18579
|
|
|
18348
18580
|
// src/commands/sensors.ts
|
|
18349
|
-
import { execFile as
|
|
18350
|
-
import { existsSync as
|
|
18351
|
-
import { chmod as chmod3, mkdir as
|
|
18352
|
-
import
|
|
18353
|
-
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";
|
|
18354
18586
|
import "commander";
|
|
18355
18587
|
import {
|
|
18356
18588
|
extractSensorExamples as extractSensorExamples2,
|
|
18357
|
-
findProjectRoot as
|
|
18589
|
+
findProjectRoot as findProjectRoot54,
|
|
18358
18590
|
isRetiredMemory as isRetiredMemory4,
|
|
18359
18591
|
judgeProposedSensor as judgeProposedSensor2,
|
|
18360
18592
|
loadConfig as loadConfig16,
|
|
@@ -18366,13 +18598,13 @@ import {
|
|
|
18366
18598
|
sensorPatternBrittleness as sensorPatternBrittleness2,
|
|
18367
18599
|
sensorSelfCheck as sensorSelfCheck2,
|
|
18368
18600
|
scannableSensorTargets,
|
|
18369
|
-
serializeMemory as
|
|
18601
|
+
serializeMemory as serializeMemory29
|
|
18370
18602
|
} from "@hivelore/core";
|
|
18371
|
-
var
|
|
18603
|
+
var exec3 = promisify5(execFile5);
|
|
18372
18604
|
function registerSensors(program2) {
|
|
18373
18605
|
const sensors = program2.command("sensors").description("Operate executable sensors derived from Hivelore memories");
|
|
18374
18606
|
sensors.command("list").description("List memories carrying executable sensors").option("--json", "emit JSON", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
18375
|
-
const root =
|
|
18607
|
+
const root = findProjectRoot54(opts.dir);
|
|
18376
18608
|
const paths = resolveHaivePaths49(root);
|
|
18377
18609
|
const rows = await sensorRows(paths);
|
|
18378
18610
|
if (opts.json) {
|
|
@@ -18401,10 +18633,10 @@ function registerSensors(program2) {
|
|
|
18401
18633
|
sensors.command("check").description(
|
|
18402
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."
|
|
18403
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) => {
|
|
18404
|
-
const root =
|
|
18636
|
+
const root = findProjectRoot54(opts.dir);
|
|
18405
18637
|
const paths = resolveHaivePaths49(root);
|
|
18406
18638
|
const memories = await runnableSensorMemories(paths);
|
|
18407
|
-
const diff = opts.diffFile ? await
|
|
18639
|
+
const diff = opts.diffFile ? await readFile26(path56.resolve(root, opts.diffFile), "utf8") : await stagedDiff(root);
|
|
18408
18640
|
const targets = scannableSensorTargets(diff);
|
|
18409
18641
|
const hits = runSensors3(memories, targets);
|
|
18410
18642
|
const config = await loadConfig16(paths);
|
|
@@ -18479,9 +18711,9 @@ function registerSensors(program2) {
|
|
|
18479
18711
|
process.exitCode = 1;
|
|
18480
18712
|
return;
|
|
18481
18713
|
}
|
|
18482
|
-
const root =
|
|
18714
|
+
const root = findProjectRoot54(opts.dir);
|
|
18483
18715
|
const paths = resolveHaivePaths49(root);
|
|
18484
|
-
const loaded =
|
|
18716
|
+
const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
|
|
18485
18717
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
18486
18718
|
if (!found) {
|
|
18487
18719
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -18531,7 +18763,7 @@ function registerSensors(program2) {
|
|
|
18531
18763
|
},
|
|
18532
18764
|
body: found.memory.body
|
|
18533
18765
|
};
|
|
18534
|
-
await writeFile39(found.filePath,
|
|
18766
|
+
await writeFile39(found.filePath, serializeMemory29(next), "utf8");
|
|
18535
18767
|
ui.success(`Updated ${id}: sensor severity=${severity}`);
|
|
18536
18768
|
if (sensor.pattern) ui.info(`pattern=${JSON.stringify(sensor.pattern)}`);
|
|
18537
18769
|
ui.info(`message=${sensor.message}`);
|
|
@@ -18548,9 +18780,9 @@ function registerSensors(program2) {
|
|
|
18548
18780
|
process.exitCode = 1;
|
|
18549
18781
|
return;
|
|
18550
18782
|
}
|
|
18551
|
-
const root =
|
|
18783
|
+
const root = findProjectRoot54(opts.dir);
|
|
18552
18784
|
const paths = resolveHaivePaths49(root);
|
|
18553
|
-
const loaded =
|
|
18785
|
+
const loaded = existsSync78(paths.memoriesDir) ? await loadMemoriesFromDir41(paths.memoriesDir) : [];
|
|
18554
18786
|
const found = loaded.find(({ memory: memory2 }) => memory2.frontmatter.id === id);
|
|
18555
18787
|
if (!found) {
|
|
18556
18788
|
ui.error(`No memory found with id ${id}`);
|
|
@@ -18587,7 +18819,7 @@ function registerSensors(program2) {
|
|
|
18587
18819
|
process.exitCode = 1;
|
|
18588
18820
|
return;
|
|
18589
18821
|
}
|
|
18590
|
-
await writeFile39(found.filePath,
|
|
18822
|
+
await writeFile39(found.filePath, serializeMemory29({ frontmatter: { ...found.memory.frontmatter, sensor }, body: found.memory.body }), "utf8");
|
|
18591
18823
|
ui.success(`Sensor accepted (${severity}) on ${id}`);
|
|
18592
18824
|
ui.info(`pattern=${JSON.stringify(opts.pattern)}${opts.absent ? ` absent=${JSON.stringify(opts.absent)}` : ""}`);
|
|
18593
18825
|
ui.info(
|
|
@@ -18601,16 +18833,16 @@ function registerSensors(program2) {
|
|
|
18601
18833
|
process.exitCode = 1;
|
|
18602
18834
|
return;
|
|
18603
18835
|
}
|
|
18604
|
-
const root =
|
|
18836
|
+
const root = findProjectRoot54(opts.dir);
|
|
18605
18837
|
const paths = resolveHaivePaths49(root);
|
|
18606
18838
|
const rows = await sensorRows(paths);
|
|
18607
|
-
const outDir =
|
|
18608
|
-
await
|
|
18609
|
-
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");
|
|
18610
18842
|
const content = format === "grep" ? renderGrepScript(rows) : JSON.stringify({ sensors: rows }, null, 2) + "\n";
|
|
18611
18843
|
await writeFile39(outPath, content, "utf8");
|
|
18612
18844
|
if (format === "grep") await chmod3(outPath, 493);
|
|
18613
|
-
ui.success(`Exported ${rows.length} sensor(s): ${
|
|
18845
|
+
ui.success(`Exported ${rows.length} sensor(s): ${path56.relative(root, outPath)}`);
|
|
18614
18846
|
});
|
|
18615
18847
|
}
|
|
18616
18848
|
function deriveProposedMessage(body, pattern, absent) {
|
|
@@ -18643,7 +18875,7 @@ async function sensorRows(paths) {
|
|
|
18643
18875
|
});
|
|
18644
18876
|
}
|
|
18645
18877
|
async function runnableSensorMemories(paths, regexOnly = true) {
|
|
18646
|
-
if (!
|
|
18878
|
+
if (!existsSync78(paths.memoriesDir)) return [];
|
|
18647
18879
|
const loaded = await loadMemoriesFromDir41(paths.memoriesDir);
|
|
18648
18880
|
return loaded.map(({ memory: memory2 }) => memory2).filter((memory2) => {
|
|
18649
18881
|
const sensor = memory2.frontmatter.sensor;
|
|
@@ -18654,7 +18886,7 @@ async function runnableSensorMemories(paths, regexOnly = true) {
|
|
|
18654
18886
|
}
|
|
18655
18887
|
async function runCommandSensor(spec, root) {
|
|
18656
18888
|
try {
|
|
18657
|
-
await
|
|
18889
|
+
await exec3("bash", ["-c", spec.command], { cwd: root, timeout: 12e4, maxBuffer: 8 * 1024 * 1024 });
|
|
18658
18890
|
return false;
|
|
18659
18891
|
} catch {
|
|
18660
18892
|
return true;
|
|
@@ -18662,7 +18894,7 @@ async function runCommandSensor(spec, root) {
|
|
|
18662
18894
|
}
|
|
18663
18895
|
async function stagedDiff(root) {
|
|
18664
18896
|
try {
|
|
18665
|
-
const { stdout } = await
|
|
18897
|
+
const { stdout } = await exec3("git", ["diff", "--cached"], { cwd: root });
|
|
18666
18898
|
return stdout;
|
|
18667
18899
|
} catch (err) {
|
|
18668
18900
|
throw new Error(`git diff --cached failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -18693,19 +18925,19 @@ function shellQuote(value) {
|
|
|
18693
18925
|
}
|
|
18694
18926
|
|
|
18695
18927
|
// src/commands/ingest.ts
|
|
18696
|
-
import { existsSync as
|
|
18697
|
-
import { mkdir as
|
|
18698
|
-
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";
|
|
18699
18931
|
import "commander";
|
|
18700
18932
|
import {
|
|
18701
18933
|
draftsFromFindings as draftsFromFindings2,
|
|
18702
18934
|
filterNewDrafts as filterNewDrafts2,
|
|
18703
|
-
findProjectRoot as
|
|
18935
|
+
findProjectRoot as findProjectRoot55,
|
|
18704
18936
|
loadMemoriesFromDir as loadMemoriesFromDir43,
|
|
18705
|
-
memoryFilePath as
|
|
18937
|
+
memoryFilePath as memoryFilePath11,
|
|
18706
18938
|
parseFindings as parseFindings2,
|
|
18707
18939
|
resolveHaivePaths as resolveHaivePaths50,
|
|
18708
|
-
serializeMemory as
|
|
18940
|
+
serializeMemory as serializeMemory30
|
|
18709
18941
|
} from "@hivelore/core";
|
|
18710
18942
|
var SEVERITIES = ["info", "minor", "major", "critical", "blocker"];
|
|
18711
18943
|
function registerIngest(program2) {
|
|
@@ -18729,9 +18961,9 @@ function registerIngest(program2) {
|
|
|
18729
18961
|
process.exitCode = 1;
|
|
18730
18962
|
return;
|
|
18731
18963
|
}
|
|
18732
|
-
const root =
|
|
18964
|
+
const root = findProjectRoot55(opts.dir);
|
|
18733
18965
|
const paths = resolveHaivePaths50(root);
|
|
18734
|
-
if (!
|
|
18966
|
+
if (!existsSync79(paths.haiveDir)) {
|
|
18735
18967
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
18736
18968
|
process.exitCode = 1;
|
|
18737
18969
|
return;
|
|
@@ -18752,14 +18984,14 @@ function registerIngest(program2) {
|
|
|
18752
18984
|
process.exitCode = 1;
|
|
18753
18985
|
return;
|
|
18754
18986
|
}
|
|
18755
|
-
const reportPath =
|
|
18756
|
-
if (!
|
|
18987
|
+
const reportPath = path57.resolve(root, file);
|
|
18988
|
+
if (!existsSync79(reportPath)) {
|
|
18757
18989
|
ui.error(`Report file not found: ${reportPath}`);
|
|
18758
18990
|
process.exitCode = 1;
|
|
18759
18991
|
return;
|
|
18760
18992
|
}
|
|
18761
18993
|
try {
|
|
18762
|
-
raw = await
|
|
18994
|
+
raw = await readFile27(reportPath, "utf8");
|
|
18763
18995
|
} catch (err) {
|
|
18764
18996
|
ui.error(`Could not read ${reportPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
18765
18997
|
process.exitCode = 1;
|
|
@@ -18785,7 +19017,7 @@ function registerIngest(program2) {
|
|
|
18785
19017
|
process.exitCode = 1;
|
|
18786
19018
|
return;
|
|
18787
19019
|
}
|
|
18788
|
-
const existing =
|
|
19020
|
+
const existing = existsSync79(paths.memoriesDir) ? await loadMemoriesFromDir43(paths.memoriesDir) : [];
|
|
18789
19021
|
const existingTopics = new Set(
|
|
18790
19022
|
existing.map(({ memory: memory2 }) => memory2.frontmatter.topic).filter((t) => Boolean(t))
|
|
18791
19023
|
);
|
|
@@ -18847,14 +19079,14 @@ function registerIngest(program2) {
|
|
|
18847
19079
|
await writeDraft2(paths, draft);
|
|
18848
19080
|
created++;
|
|
18849
19081
|
}
|
|
18850
|
-
ui.success(`Created ${created} proposed memory(ies) under ${
|
|
19082
|
+
ui.success(`Created ${created} proposed memory(ies) under ${path57.relative(root, paths.memoriesDir)}/`);
|
|
18851
19083
|
ui.info("Review with `hivelore memory pending`; promote sensors with `hivelore sensors promote <id> --yes`.");
|
|
18852
19084
|
});
|
|
18853
19085
|
}
|
|
18854
19086
|
async function writeDraft2(paths, draft) {
|
|
18855
|
-
const file =
|
|
18856
|
-
await
|
|
18857
|
-
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");
|
|
18858
19090
|
return file;
|
|
18859
19091
|
}
|
|
18860
19092
|
async function fetchSonarIssues(opts) {
|
|
@@ -18895,11 +19127,11 @@ async function fetchSonarIssues(opts) {
|
|
|
18895
19127
|
}
|
|
18896
19128
|
|
|
18897
19129
|
// src/commands/dashboard.ts
|
|
18898
|
-
import { existsSync as
|
|
19130
|
+
import { existsSync as existsSync80 } from "fs";
|
|
18899
19131
|
import "commander";
|
|
18900
19132
|
import {
|
|
18901
19133
|
buildDashboard,
|
|
18902
|
-
findProjectRoot as
|
|
19134
|
+
findProjectRoot as findProjectRoot56,
|
|
18903
19135
|
loadConfig as loadConfig17,
|
|
18904
19136
|
loadMemoriesFromDir as loadMemoriesFromDir44,
|
|
18905
19137
|
loadPreventionEvents as loadPreventionEvents4,
|
|
@@ -18910,14 +19142,14 @@ function registerDashboard(program2) {
|
|
|
18910
19142
|
program2.command("dashboard").description(
|
|
18911
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."
|
|
18912
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) => {
|
|
18913
|
-
const root =
|
|
19145
|
+
const root = findProjectRoot56(opts.dir);
|
|
18914
19146
|
const paths = resolveHaivePaths51(root);
|
|
18915
|
-
if (!
|
|
19147
|
+
if (!existsSync80(paths.haiveDir)) {
|
|
18916
19148
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
18917
19149
|
process.exitCode = 1;
|
|
18918
19150
|
return;
|
|
18919
19151
|
}
|
|
18920
|
-
const memories =
|
|
19152
|
+
const memories = existsSync80(paths.memoriesDir) ? await loadMemoriesFromDir44(paths.memoriesDir) : [];
|
|
18921
19153
|
const usage = await loadUsageIndex31(paths);
|
|
18922
19154
|
const preventionEvents = await loadPreventionEvents4(paths);
|
|
18923
19155
|
const config = await loadConfig17(paths);
|
|
@@ -19041,30 +19273,30 @@ function warnNum(n) {
|
|
|
19041
19273
|
}
|
|
19042
19274
|
|
|
19043
19275
|
// src/commands/dev-link.ts
|
|
19044
|
-
import { execFile as
|
|
19045
|
-
import { cp, readFile as
|
|
19046
|
-
import { existsSync as
|
|
19047
|
-
import
|
|
19048
|
-
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";
|
|
19049
19281
|
import "commander";
|
|
19050
|
-
import { findProjectRoot as
|
|
19051
|
-
var
|
|
19282
|
+
import { findProjectRoot as findProjectRoot57 } from "@hivelore/core";
|
|
19283
|
+
var exec4 = promisify6(execFile6);
|
|
19052
19284
|
function registerDevLink(program2) {
|
|
19053
19285
|
const dev = program2.commands.find((c) => c.name() === "dev") ?? program2.command("dev").description("Developer utilities for working on Hivelore itself.");
|
|
19054
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) => {
|
|
19055
|
-
const root =
|
|
19056
|
-
if (!
|
|
19287
|
+
const root = findProjectRoot57(opts.dir);
|
|
19288
|
+
if (!existsSync81(path58.join(root, "packages", "cli", "dist", "index.js"))) {
|
|
19057
19289
|
ui.error(`Not the Hivelore monorepo (no packages/cli/dist) at ${root}. Run \`pnpm -r build\` first, or pass --dir.`);
|
|
19058
19290
|
process.exitCode = 1;
|
|
19059
19291
|
return;
|
|
19060
19292
|
}
|
|
19061
19293
|
let globalModules;
|
|
19062
19294
|
try {
|
|
19063
|
-
globalModules = (await
|
|
19295
|
+
globalModules = (await exec4("npm", ["root", "-g"])).stdout.trim();
|
|
19064
19296
|
} catch {
|
|
19065
|
-
globalModules =
|
|
19297
|
+
globalModules = path58.join(path58.dirname(path58.dirname(process.execPath)), "lib", "node_modules");
|
|
19066
19298
|
}
|
|
19067
|
-
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) =>
|
|
19299
|
+
const scopeDirs = ["@hivelore", "@hiveai"].map((scope) => path58.join(globalModules, scope)).filter((dir) => existsSync81(dir));
|
|
19068
19300
|
if (scopeDirs.length === 0) {
|
|
19069
19301
|
ui.error(`No global @hivelore (or legacy @hiveai) install under ${globalModules}. Install once with \`npm i -g @hivelore/cli\`, then re-run.`);
|
|
19070
19302
|
process.exitCode = 1;
|
|
@@ -19072,24 +19304,24 @@ function registerDevLink(program2) {
|
|
|
19072
19304
|
}
|
|
19073
19305
|
const linked = [];
|
|
19074
19306
|
const copyDist = async (fromPkg, toDistDir) => {
|
|
19075
|
-
const from =
|
|
19076
|
-
if (!
|
|
19307
|
+
const from = path58.join(root, "packages", fromPkg, "dist");
|
|
19308
|
+
if (!existsSync81(from) || !existsSync81(path58.dirname(toDistDir))) return;
|
|
19077
19309
|
await cp(from, toDistDir, { recursive: true });
|
|
19078
|
-
linked.push(
|
|
19310
|
+
linked.push(path58.relative(globalModules, toDistDir));
|
|
19079
19311
|
};
|
|
19080
19312
|
for (const globalHive of scopeDirs) {
|
|
19081
|
-
const nestedScope =
|
|
19313
|
+
const nestedScope = path58.basename(globalHive);
|
|
19082
19314
|
for (const pkg of ["cli", "mcp"]) {
|
|
19083
|
-
await copyDist(pkg,
|
|
19315
|
+
await copyDist(pkg, path58.join(globalHive, pkg, "dist"));
|
|
19084
19316
|
for (const nested of ["core", "embeddings"]) {
|
|
19085
|
-
await copyDist(nested,
|
|
19317
|
+
await copyDist(nested, path58.join(globalHive, pkg, "node_modules", nestedScope, nested, "dist"));
|
|
19086
19318
|
}
|
|
19087
19319
|
}
|
|
19088
|
-
await copyDist("core",
|
|
19320
|
+
await copyDist("core", path58.join(globalHive, "core", "dist"));
|
|
19089
19321
|
}
|
|
19090
19322
|
let version = "unknown";
|
|
19091
19323
|
try {
|
|
19092
|
-
version = JSON.parse(await
|
|
19324
|
+
version = JSON.parse(await readFile28(path58.join(root, "package.json"), "utf8")).version ?? "unknown";
|
|
19093
19325
|
} catch {
|
|
19094
19326
|
}
|
|
19095
19327
|
if (opts.json) {
|
|
@@ -19100,27 +19332,27 @@ function registerDevLink(program2) {
|
|
|
19100
19332
|
ui.warn("Nothing linked \u2014 no matching dist targets were found in the global install.");
|
|
19101
19333
|
return;
|
|
19102
19334
|
}
|
|
19103
|
-
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(", ")}`);
|
|
19104
19336
|
for (const t of linked) console.log(` ${ui.dim("\u2192")} ${t}`);
|
|
19105
19337
|
console.log(ui.dim("The global binary now runs your local build (git hooks + MCP included)."));
|
|
19106
19338
|
});
|
|
19107
19339
|
}
|
|
19108
19340
|
|
|
19109
19341
|
// src/commands/coverage.ts
|
|
19110
|
-
import { readFile as
|
|
19111
|
-
import { existsSync as
|
|
19112
|
-
import
|
|
19342
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
19343
|
+
import { existsSync as existsSync83 } from "fs";
|
|
19344
|
+
import path59 from "path";
|
|
19113
19345
|
import "commander";
|
|
19114
19346
|
import {
|
|
19115
19347
|
findCoverageGaps,
|
|
19116
|
-
findProjectRoot as
|
|
19348
|
+
findProjectRoot as findProjectRoot58,
|
|
19117
19349
|
mergeHotFiles,
|
|
19118
19350
|
resolveHaivePaths as resolveHaivePaths52,
|
|
19119
19351
|
tallyHotFiles
|
|
19120
19352
|
} from "@hivelore/core";
|
|
19121
19353
|
async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
19122
|
-
if (!
|
|
19123
|
-
const raw = await
|
|
19354
|
+
if (!existsSync83(cacheFile)) return [];
|
|
19355
|
+
const raw = await readFile29(cacheFile, "utf8").catch(() => "");
|
|
19124
19356
|
const files = [];
|
|
19125
19357
|
for (const line of raw.split("\n")) {
|
|
19126
19358
|
const trimmed = line.trim();
|
|
@@ -19133,7 +19365,7 @@ async function readAgentHotFiles(root, cacheFile, sinceMs) {
|
|
|
19133
19365
|
}
|
|
19134
19366
|
for (const f of obs.files ?? []) {
|
|
19135
19367
|
if (typeof f !== "string" || !f) continue;
|
|
19136
|
-
const rel =
|
|
19368
|
+
const rel = path59.isAbsolute(f) ? path59.relative(root, f) : f;
|
|
19137
19369
|
if (rel.startsWith("..")) continue;
|
|
19138
19370
|
files.push(rel);
|
|
19139
19371
|
}
|
|
@@ -19154,7 +19386,7 @@ function registerCoverage(program2) {
|
|
|
19154
19386
|
program2.command("coverage").description(
|
|
19155
19387
|
"Coverage-gap report: frequently-edited files with no covering team memory (blind spots)."
|
|
19156
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) => {
|
|
19157
|
-
const root =
|
|
19389
|
+
const root = findProjectRoot58(opts.dir);
|
|
19158
19390
|
const paths = resolveHaivePaths52(root);
|
|
19159
19391
|
const minChanges = Math.max(1, parseInt(opts.minChanges ?? "3", 10) || 3);
|
|
19160
19392
|
const limit = Math.max(1, parseInt(opts.limit ?? "20", 10) || 20);
|
|
@@ -19177,7 +19409,7 @@ function registerCoverage(program2) {
|
|
|
19177
19409
|
}) : null;
|
|
19178
19410
|
const gitHotFiles = (radar?.hotFiles ?? []).filter((h) => !isNoisePath(h.path)).map((h) => ({ path: h.path, changes: h.changes, source: "git" }));
|
|
19179
19411
|
const sinceMs = Date.now() - days * 864e5;
|
|
19180
|
-
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)) : [];
|
|
19181
19413
|
const hotFiles = mergeHotFiles(gitHotFiles, agentHotFiles);
|
|
19182
19414
|
const memories = await loadMemoriesFromDir29(paths.memoriesDir);
|
|
19183
19415
|
const gaps = findCoverageGaps(hotFiles, memories, { minChanges, limit });
|
|
@@ -19215,10 +19447,10 @@ function registerCoverage(program2) {
|
|
|
19215
19447
|
|
|
19216
19448
|
// src/commands/merge-driver.ts
|
|
19217
19449
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
19218
|
-
import { readFileSync, writeFileSync, existsSync as
|
|
19219
|
-
import
|
|
19450
|
+
import { readFileSync, writeFileSync, existsSync as existsSync84 } from "fs";
|
|
19451
|
+
import path60 from "path";
|
|
19220
19452
|
import "commander";
|
|
19221
|
-
import { findProjectRoot as
|
|
19453
|
+
import { findProjectRoot as findProjectRoot59, mergeMemoryVersions } from "@hivelore/core";
|
|
19222
19454
|
var GITATTRIBUTES_MARK = "# Hivelore merge driver";
|
|
19223
19455
|
var GITATTRIBUTES_BLOCK = [
|
|
19224
19456
|
GITATTRIBUTES_MARK,
|
|
@@ -19239,7 +19471,7 @@ function registerMergeDriver(program2) {
|
|
|
19239
19471
|
}
|
|
19240
19472
|
});
|
|
19241
19473
|
cmd.command("install").description("Configure git + .gitattributes so memory-file conflicts auto-resolve").option("-d, --dir <dir>", "project root").action((opts) => {
|
|
19242
|
-
const root =
|
|
19474
|
+
const root = findProjectRoot59(opts.dir);
|
|
19243
19475
|
try {
|
|
19244
19476
|
execFileSync3("git", ["config", "merge.haive.name", "Hivelore memory merge driver"], { cwd: root });
|
|
19245
19477
|
execFileSync3("git", ["config", "merge.haive.driver", "hivelore merge-driver run %O %A %B"], { cwd: root });
|
|
@@ -19248,8 +19480,8 @@ function registerMergeDriver(program2) {
|
|
|
19248
19480
|
process.exitCode = 1;
|
|
19249
19481
|
return;
|
|
19250
19482
|
}
|
|
19251
|
-
const gaPath =
|
|
19252
|
-
let content =
|
|
19483
|
+
const gaPath = path60.join(root, ".gitattributes");
|
|
19484
|
+
let content = existsSync84(gaPath) ? readFileSync(gaPath, "utf8") : "";
|
|
19253
19485
|
if (!content.includes(GITATTRIBUTES_MARK)) {
|
|
19254
19486
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
19255
19487
|
content += GITATTRIBUTES_BLOCK + "\n";
|
|
@@ -19264,20 +19496,20 @@ function registerMergeDriver(program2) {
|
|
|
19264
19496
|
|
|
19265
19497
|
// src/commands/memory-resolve-conflict.ts
|
|
19266
19498
|
import { writeFile as writeFile41 } from "fs/promises";
|
|
19267
|
-
import { existsSync as
|
|
19499
|
+
import { existsSync as existsSync85 } from "fs";
|
|
19268
19500
|
import "commander";
|
|
19269
19501
|
import {
|
|
19270
19502
|
applyConflictResolution,
|
|
19271
|
-
findProjectRoot as
|
|
19503
|
+
findProjectRoot as findProjectRoot60,
|
|
19272
19504
|
planConflictResolution as planConflictResolution2,
|
|
19273
19505
|
resolveHaivePaths as resolveHaivePaths53,
|
|
19274
|
-
serializeMemory as
|
|
19506
|
+
serializeMemory as serializeMemory31
|
|
19275
19507
|
} from "@hivelore/core";
|
|
19276
19508
|
function registerMemoryResolveConflict(memory2) {
|
|
19277
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) => {
|
|
19278
|
-
const root =
|
|
19510
|
+
const root = findProjectRoot60(opts.dir);
|
|
19279
19511
|
const paths = resolveHaivePaths53(root);
|
|
19280
|
-
if (!
|
|
19512
|
+
if (!existsSync85(paths.memoriesDir)) {
|
|
19281
19513
|
ui.error(`No .ai/memories at ${root}.`);
|
|
19282
19514
|
process.exitCode = 1;
|
|
19283
19515
|
return;
|
|
@@ -19317,12 +19549,12 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
19317
19549
|
}
|
|
19318
19550
|
await writeFile41(
|
|
19319
19551
|
winner.filePath,
|
|
19320
|
-
|
|
19552
|
+
serializeMemory31({ frontmatter: applied.winner, body: winner.memory.body }),
|
|
19321
19553
|
"utf8"
|
|
19322
19554
|
);
|
|
19323
19555
|
await writeFile41(
|
|
19324
19556
|
loser.filePath,
|
|
19325
|
-
|
|
19557
|
+
serializeMemory31({ frontmatter: applied.loser, body: loser.memory.body }),
|
|
19326
19558
|
"utf8"
|
|
19327
19559
|
);
|
|
19328
19560
|
if (!opts.json) {
|
|
@@ -19332,26 +19564,26 @@ function registerMemoryResolveConflict(memory2) {
|
|
|
19332
19564
|
}
|
|
19333
19565
|
|
|
19334
19566
|
// src/commands/memory-seed-git.ts
|
|
19335
|
-
import { execFile as
|
|
19336
|
-
import { mkdir as
|
|
19337
|
-
import { existsSync as
|
|
19338
|
-
import
|
|
19339
|
-
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";
|
|
19340
19572
|
import "commander";
|
|
19341
19573
|
import {
|
|
19342
|
-
buildFrontmatter as
|
|
19343
|
-
findProjectRoot as
|
|
19344
|
-
memoryFilePath as
|
|
19574
|
+
buildFrontmatter as buildFrontmatter11,
|
|
19575
|
+
findProjectRoot as findProjectRoot61,
|
|
19576
|
+
memoryFilePath as memoryFilePath12,
|
|
19345
19577
|
proposeSeedsFromCommits as proposeSeedsFromCommits2,
|
|
19346
19578
|
resolveHaivePaths as resolveHaivePaths54,
|
|
19347
|
-
serializeMemory as
|
|
19579
|
+
serializeMemory as serializeMemory33
|
|
19348
19580
|
} from "@hivelore/core";
|
|
19349
|
-
var
|
|
19581
|
+
var exec5 = promisify7(execFile7);
|
|
19350
19582
|
function registerMemorySeedGit(memory2) {
|
|
19351
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) => {
|
|
19352
|
-
const root =
|
|
19584
|
+
const root = findProjectRoot61(opts.dir);
|
|
19353
19585
|
const paths = resolveHaivePaths54(root);
|
|
19354
|
-
if (!
|
|
19586
|
+
if (!existsSync86(paths.haiveDir)) {
|
|
19355
19587
|
ui.error(`No .ai/ found at ${root}. Run \`hivelore init\` first.`);
|
|
19356
19588
|
process.exitCode = 1;
|
|
19357
19589
|
return;
|
|
@@ -19379,7 +19611,7 @@ function registerMemorySeedGit(memory2) {
|
|
|
19379
19611
|
let written = 0;
|
|
19380
19612
|
for (const p of proposals) {
|
|
19381
19613
|
const fm = {
|
|
19382
|
-
...
|
|
19614
|
+
...buildFrontmatter11({
|
|
19383
19615
|
type: "attempt",
|
|
19384
19616
|
slug: p.slug,
|
|
19385
19617
|
scope: opts.scope ?? "team",
|
|
@@ -19395,10 +19627,10 @@ function registerMemorySeedGit(memory2) {
|
|
|
19395
19627
|
|
|
19396
19628
|
_Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delete) \u2014 not yet authoritative._
|
|
19397
19629
|
`;
|
|
19398
|
-
const file =
|
|
19399
|
-
if (
|
|
19400
|
-
await
|
|
19401
|
-
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");
|
|
19402
19634
|
written += 1;
|
|
19403
19635
|
}
|
|
19404
19636
|
if (!opts.json) {
|
|
@@ -19408,7 +19640,7 @@ _Seeded from git ${p.kind} commit ${p.source_sha}. Review and validate (or delet
|
|
|
19408
19640
|
}
|
|
19409
19641
|
async function readCommits(root, days) {
|
|
19410
19642
|
try {
|
|
19411
|
-
const { stdout } = await
|
|
19643
|
+
const { stdout } = await exec5(
|
|
19412
19644
|
"git",
|
|
19413
19645
|
["log", `--since=${days}.days.ago`, "--name-only", "--pretty=format:%x1f%h%x1f%s", "-n", "500"],
|
|
19414
19646
|
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
@@ -19429,11 +19661,11 @@ async function readCommits(root, days) {
|
|
|
19429
19661
|
}
|
|
19430
19662
|
|
|
19431
19663
|
// src/commands/bridges.ts
|
|
19432
|
-
import { existsSync as
|
|
19433
|
-
import
|
|
19664
|
+
import { existsSync as existsSync87 } from "fs";
|
|
19665
|
+
import path63 from "path";
|
|
19434
19666
|
import "commander";
|
|
19435
19667
|
import {
|
|
19436
|
-
findProjectRoot as
|
|
19668
|
+
findProjectRoot as findProjectRoot62,
|
|
19437
19669
|
resolveHaivePaths as resolveHaivePaths55,
|
|
19438
19670
|
BRIDGE_TARGET_PATH as BRIDGE_TARGET_PATH3,
|
|
19439
19671
|
BRIDGE_TARGETS as BRIDGE_TARGETS3
|
|
@@ -19448,10 +19680,10 @@ function registerBridges(program2) {
|
|
|
19448
19680
|
"--only <targets>",
|
|
19449
19681
|
"comma-separated list of targets to generate (e.g. cline,windsurf,agents)"
|
|
19450
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) => {
|
|
19451
|
-
const root =
|
|
19683
|
+
const root = findProjectRoot62(opts.dir);
|
|
19452
19684
|
const paths = resolveHaivePaths55(root);
|
|
19453
19685
|
const dryRun = opts.dryRun === true;
|
|
19454
|
-
if (!
|
|
19686
|
+
if (!existsSync87(paths.memoriesDir)) {
|
|
19455
19687
|
ui.warn(`No .ai/memories at ${root}. Run \`hivelore init\` first.`);
|
|
19456
19688
|
process.exitCode = 1;
|
|
19457
19689
|
return;
|
|
@@ -19470,7 +19702,7 @@ function registerBridges(program2) {
|
|
|
19470
19702
|
targets = BRIDGE_TARGETS3;
|
|
19471
19703
|
} else {
|
|
19472
19704
|
targets = BRIDGE_TARGETS3.filter(
|
|
19473
|
-
(t) =>
|
|
19705
|
+
(t) => existsSync87(path63.join(root, BRIDGE_TARGET_PATH3[t]))
|
|
19474
19706
|
);
|
|
19475
19707
|
if (targets.length === 0) {
|
|
19476
19708
|
ui.info(
|
|
@@ -19499,7 +19731,7 @@ function registerBridges(program2) {
|
|
|
19499
19731
|
console.log(ui.dim(`bridges: ${parts.join(" \xB7 ") || "nothing to do"}`));
|
|
19500
19732
|
});
|
|
19501
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) => {
|
|
19502
|
-
const root =
|
|
19734
|
+
const root = findProjectRoot62(opts.dir);
|
|
19503
19735
|
const paths = resolveHaivePaths55(root);
|
|
19504
19736
|
const statuses = await getBridgeFileStatuses(root, paths, {
|
|
19505
19737
|
targets: BRIDGE_TARGETS3,
|
|
@@ -19517,13 +19749,14 @@ function registerBridges(program2) {
|
|
|
19517
19749
|
}
|
|
19518
19750
|
|
|
19519
19751
|
// src/index.ts
|
|
19520
|
-
var program = new
|
|
19521
|
-
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);
|
|
19522
19754
|
registerInit(program);
|
|
19523
19755
|
registerWelcome(program);
|
|
19524
19756
|
registerResolveProject(program);
|
|
19525
19757
|
registerRuntime(program);
|
|
19526
19758
|
registerEnforce(program);
|
|
19759
|
+
registerRelease(program);
|
|
19527
19760
|
registerRun(program);
|
|
19528
19761
|
registerAgent(program);
|
|
19529
19762
|
registerSensors(program);
|