@hivelore/cli 0.37.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VLRQ4MRO.js → chunk-I4VELI5K.js} +366 -188
- package/dist/chunk-I4VELI5K.js.map +1 -0
- package/dist/index.js +366 -232
- package/dist/index.js.map +1 -1
- package/dist/{server-J6TDFG2C.js → server-47VOVJJT.js} +10 -4
- package/package.json +4 -4
- package/dist/chunk-VLRQ4MRO.js.map +0 -1
- /package/dist/{server-J6TDFG2C.js.map → server-47VOVJJT.js.map} +0 -0
|
@@ -144,41 +144,53 @@ import {
|
|
|
144
144
|
serializeMemory as serializeMemory7
|
|
145
145
|
} from "@hivelore/core";
|
|
146
146
|
import { z as z15 } from "zod";
|
|
147
|
-
import { existsSync as existsSync17 } from "fs";
|
|
147
|
+
import { existsSync as existsSync17, statSync } from "fs";
|
|
148
148
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile10 } from "fs/promises";
|
|
149
149
|
import path7 from "path";
|
|
150
|
+
import { z as z17 } from "zod";
|
|
151
|
+
import {
|
|
152
|
+
buildProposeCommand,
|
|
153
|
+
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
154
|
+
normalizeFramework,
|
|
155
|
+
parseLessonFields,
|
|
156
|
+
pickTestFramework,
|
|
157
|
+
scaffoldPostIncidentTest
|
|
158
|
+
} from "@hivelore/core";
|
|
159
|
+
import { existsSync as existsSync18 } from "fs";
|
|
160
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
161
|
+
import path8 from "path";
|
|
150
162
|
import {
|
|
151
163
|
draftsFromFindings,
|
|
152
164
|
filterNewDrafts,
|
|
153
|
-
loadMemoriesFromDir as
|
|
165
|
+
loadMemoriesFromDir as loadMemoriesFromDir15,
|
|
154
166
|
memoryFilePath as memoryFilePath3,
|
|
155
167
|
parseFindings,
|
|
156
168
|
serializeMemory as serializeMemory9
|
|
157
169
|
} from "@hivelore/core";
|
|
158
|
-
import { z as
|
|
159
|
-
import { writeFile as
|
|
160
|
-
import { existsSync as
|
|
161
|
-
import
|
|
170
|
+
import { z as z18 } from "zod";
|
|
171
|
+
import { writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
172
|
+
import { existsSync as existsSync20 } from "fs";
|
|
173
|
+
import path10 from "path";
|
|
162
174
|
import {
|
|
163
175
|
buildFrontmatter as buildFrontmatter3,
|
|
164
|
-
loadMemoriesFromDir as
|
|
176
|
+
loadMemoriesFromDir as loadMemoriesFromDir16,
|
|
165
177
|
memoryFilePath as memoryFilePath4,
|
|
166
178
|
serializeMemory as serializeMemory10
|
|
167
179
|
} from "@hivelore/core";
|
|
168
|
-
import { z as
|
|
180
|
+
import { z as z19 } from "zod";
|
|
169
181
|
import {
|
|
170
182
|
appendUsageEvent,
|
|
171
183
|
appendRuntimeJournalEntry,
|
|
172
184
|
loadConfig as loadConfig2,
|
|
173
185
|
writeSessionHandoff
|
|
174
186
|
} from "@hivelore/core";
|
|
175
|
-
import { mkdir as
|
|
176
|
-
import { existsSync as
|
|
177
|
-
import
|
|
187
|
+
import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
|
|
188
|
+
import { existsSync as existsSync19 } from "fs";
|
|
189
|
+
import path9 from "path";
|
|
178
190
|
import { execSync as execSync2 } from "child_process";
|
|
179
|
-
import { readFile as
|
|
180
|
-
import { existsSync as
|
|
181
|
-
import
|
|
191
|
+
import { readFile as readFile7, writeFile as writeFile14, readdir as readdir4 } from "fs/promises";
|
|
192
|
+
import { existsSync as existsSync22 } from "fs";
|
|
193
|
+
import path12 from "path";
|
|
182
194
|
import {
|
|
183
195
|
allocateBudget,
|
|
184
196
|
assessBootstrapState,
|
|
@@ -202,7 +214,7 @@ import {
|
|
|
202
214
|
loadConfig as loadConfig3,
|
|
203
215
|
memoryHasExcludedTag,
|
|
204
216
|
hashProjectContext,
|
|
205
|
-
loadMemoriesFromDir as
|
|
217
|
+
loadMemoriesFromDir as loadMemoriesFromDir17,
|
|
206
218
|
loadPreventionEvents,
|
|
207
219
|
loadUsageIndex as loadUsageIndex8,
|
|
208
220
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
@@ -220,10 +232,10 @@ import {
|
|
|
220
232
|
truncateToTokens,
|
|
221
233
|
writeBriefingMarker
|
|
222
234
|
} from "@hivelore/core";
|
|
223
|
-
import { z as
|
|
224
|
-
import { readdir as readdir3, readFile as
|
|
225
|
-
import { existsSync as
|
|
226
|
-
import
|
|
235
|
+
import { z as z20 } from "zod";
|
|
236
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
237
|
+
import { existsSync as existsSync21 } from "fs";
|
|
238
|
+
import path11 from "path";
|
|
227
239
|
import {
|
|
228
240
|
classifyMemoryPriority as coreClassifyPriority,
|
|
229
241
|
isGlobPath,
|
|
@@ -231,17 +243,17 @@ import {
|
|
|
231
243
|
priorityRank as corePriorityRank
|
|
232
244
|
} from "@hivelore/core";
|
|
233
245
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hivelore/core";
|
|
234
|
-
import { z as z20 } from "zod";
|
|
235
|
-
import { existsSync as existsSync22 } from "fs";
|
|
236
|
-
import { loadMemoriesFromDir as loadMemoriesFromDir17 } from "@hivelore/core";
|
|
237
246
|
import { z as z21 } from "zod";
|
|
238
247
|
import { existsSync as existsSync23 } from "fs";
|
|
239
248
|
import { loadMemoriesFromDir as loadMemoriesFromDir18 } from "@hivelore/core";
|
|
240
249
|
import { z as z22 } from "zod";
|
|
250
|
+
import { existsSync as existsSync24 } from "fs";
|
|
251
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir19 } from "@hivelore/core";
|
|
241
252
|
import { z as z23 } from "zod";
|
|
242
253
|
import { z as z24 } from "zod";
|
|
254
|
+
import { z as z25 } from "zod";
|
|
243
255
|
import { loadCodeMap as loadCodeMap3 } from "@hivelore/core";
|
|
244
|
-
import { existsSync as
|
|
256
|
+
import { existsSync as existsSync25 } from "fs";
|
|
245
257
|
import {
|
|
246
258
|
addedLinesFromDiff,
|
|
247
259
|
BRIDGE_TARGET_PATH,
|
|
@@ -251,7 +263,7 @@ import {
|
|
|
251
263
|
diffHasDistinctiveOverlap,
|
|
252
264
|
getUsage as getUsage7,
|
|
253
265
|
isRetiredMemory as isRetiredMemory2,
|
|
254
|
-
loadMemoriesFromDir as
|
|
266
|
+
loadMemoriesFromDir as loadMemoriesFromDir20,
|
|
255
267
|
loadUsageIndex as loadUsageIndex9,
|
|
256
268
|
literalMatchesAnyToken as literalMatchesAnyToken3,
|
|
257
269
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3,
|
|
@@ -260,42 +272,42 @@ import {
|
|
|
260
272
|
sensorTargetsFromDiff,
|
|
261
273
|
tokenizeQuery as tokenizeQuery3
|
|
262
274
|
} from "@hivelore/core";
|
|
263
|
-
import { z as
|
|
264
|
-
import { existsSync as
|
|
275
|
+
import { z as z26 } from "zod";
|
|
276
|
+
import { existsSync as existsSync26 } from "fs";
|
|
265
277
|
import {
|
|
266
|
-
loadMemoriesFromDir as
|
|
278
|
+
loadMemoriesFromDir as loadMemoriesFromDir21,
|
|
267
279
|
tokenizeQuery as tokenizeQuery4
|
|
268
280
|
} from "@hivelore/core";
|
|
269
|
-
import { z as z26 } from "zod";
|
|
270
|
-
import { pathsOverlap as pathsOverlap2 } from "@hivelore/core";
|
|
271
281
|
import { z as z27 } from "zod";
|
|
272
|
-
import {
|
|
282
|
+
import { pathsOverlap as pathsOverlap2 } from "@hivelore/core";
|
|
283
|
+
import { z as z28 } from "zod";
|
|
284
|
+
import { existsSync as existsSync27 } from "fs";
|
|
273
285
|
import {
|
|
274
286
|
findLexicalConflictPairs,
|
|
275
287
|
findTopicStatusConflictPairs,
|
|
276
|
-
loadMemoriesFromDir as
|
|
288
|
+
loadMemoriesFromDir as loadMemoriesFromDir22,
|
|
277
289
|
planConflictResolution
|
|
278
290
|
} from "@hivelore/core";
|
|
279
|
-
import { z as z28 } from "zod";
|
|
280
|
-
import { resolveProjectInfo } from "@hivelore/core";
|
|
281
291
|
import { z as z29 } from "zod";
|
|
282
|
-
import {
|
|
292
|
+
import { resolveProjectInfo } from "@hivelore/core";
|
|
283
293
|
import { z as z30 } from "zod";
|
|
284
|
-
import {
|
|
285
|
-
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hivelore/core";
|
|
294
|
+
import { MemoryTypeSchema, suggestTopicKey } from "@hivelore/core";
|
|
286
295
|
import { z as z31 } from "zod";
|
|
287
|
-
import { z as z32 } from "zod";
|
|
288
|
-
import { readFile as readFile7, readdir as readdir5 } from "fs/promises";
|
|
289
296
|
import { existsSync as existsSync28 } from "fs";
|
|
297
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hivelore/core";
|
|
298
|
+
import { z as z32 } from "zod";
|
|
299
|
+
import { z as z33 } from "zod";
|
|
300
|
+
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
301
|
+
import { existsSync as existsSync29 } from "fs";
|
|
290
302
|
import {
|
|
291
303
|
assessBootstrapState as assessBootstrapState2,
|
|
292
304
|
loadCodeMap as loadCodeMap4,
|
|
293
|
-
loadMemoriesFromDir as
|
|
305
|
+
loadMemoriesFromDir as loadMemoriesFromDir24,
|
|
294
306
|
renderBootstrapChecklist as renderBootstrapChecklist2
|
|
295
307
|
} from "@hivelore/core";
|
|
296
|
-
import { z as z33 } from "zod";
|
|
297
308
|
import { z as z34 } from "zod";
|
|
298
309
|
import { z as z35 } from "zod";
|
|
310
|
+
import { z as z36 } from "zod";
|
|
299
311
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
300
312
|
function createContext(options = {}) {
|
|
301
313
|
const env = options.env ?? process.env;
|
|
@@ -1380,6 +1392,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1380
1392
|
if (!found) {
|
|
1381
1393
|
throw new Error(`No memory found with id ${input.memory_id}`);
|
|
1382
1394
|
}
|
|
1395
|
+
const personalScopeNudge = found.memory.frontmatter.scope === "personal" ? ` Note: this lesson is personal-scoped, so the sensor guards only YOUR machine (personal memories are gitignored). Promote it so the gate travels with the repo: hivelore memory promote ${input.memory_id}.` : "";
|
|
1383
1396
|
if (kind !== "regex") {
|
|
1384
1397
|
const verdictCmd = runCommandForValidation(input.command.trim(), ctx.paths.root, input.timeout_ms);
|
|
1385
1398
|
const anchorPathsCmd = input.paths.length > 0 ? input.paths : found.memory.frontmatter.anchor.paths;
|
|
@@ -1414,7 +1427,7 @@ ${verdictCmd.detail}`,
|
|
|
1414
1427
|
accepted: true,
|
|
1415
1428
|
memory_id: input.memory_id,
|
|
1416
1429
|
severity: input.severity,
|
|
1417
|
-
guidance: verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail})
|
|
1430
|
+
guidance: (verdictCmd.status === "passed" ? "Command oracle passes on the current tree; the gate now runs it when the diff touches the sensor's paths (requires enforcement.runCommandSensors=true)." : `Accepted at warn severity, but note: ${verdictCmd.status} on the current tree (${verdictCmd.detail}).`) + personalScopeNudge,
|
|
1418
1431
|
self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: null, fired_on: [] }
|
|
1419
1432
|
};
|
|
1420
1433
|
}
|
|
@@ -1463,6 +1476,7 @@ ${verdictCmd.detail}`,
|
|
|
1463
1476
|
accepted: true,
|
|
1464
1477
|
memory_id: input.memory_id,
|
|
1465
1478
|
severity: input.severity,
|
|
1479
|
+
...personalScopeNudge ? { guidance: personalScopeNudge.trim() } : {},
|
|
1466
1480
|
self_check,
|
|
1467
1481
|
file_path: found.filePath
|
|
1468
1482
|
};
|
|
@@ -1471,7 +1485,9 @@ var MemTriedInputSchema = {
|
|
|
1471
1485
|
what: z16.string().min(1).describe("Brief description of the approach that was tried"),
|
|
1472
1486
|
why_failed: z16.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
1473
1487
|
instead: z16.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
1474
|
-
scope: z16.enum(["personal", "team", "module"]).
|
|
1488
|
+
scope: z16.enum(["personal", "team", "module"]).optional().describe(
|
|
1489
|
+
"Visibility scope. Defaults to personal \u2014 EXCEPT when a one-shot `sensor` is attached: an enforced lesson is team truth (the sensor must travel to every machine and CI), so it defaults to team. Pass scope explicitly to override."
|
|
1490
|
+
),
|
|
1475
1491
|
module: z16.string().optional().describe("Module name (required when scope=module)"),
|
|
1476
1492
|
tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
|
|
1477
1493
|
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
@@ -1495,10 +1511,11 @@ async function memTried(input, ctx) {
|
|
|
1495
1511
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1496
1512
|
}
|
|
1497
1513
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
1514
|
+
const scope = input.scope ?? (input.sensor ? "team" : "personal");
|
|
1498
1515
|
const baseFm = buildFrontmatter2({
|
|
1499
1516
|
type: "attempt",
|
|
1500
1517
|
slug,
|
|
1501
|
-
scope
|
|
1518
|
+
scope,
|
|
1502
1519
|
module: input.module,
|
|
1503
1520
|
tags: input.tags,
|
|
1504
1521
|
paths: input.paths,
|
|
@@ -1546,7 +1563,7 @@ async function memTried(input, ctx) {
|
|
|
1546
1563
|
...verdict.reason ? { reason: verdict.reason } : {},
|
|
1547
1564
|
...verdict.guidance ? { guidance: verdict.guidance } : {}
|
|
1548
1565
|
},
|
|
1549
|
-
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.`
|
|
1566
|
+
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.`) + (input.scope === void 0 ? " Saved team-scoped (an enforced lesson must travel with the repo) \u2014 pass scope:'personal' to keep it private." : "")
|
|
1550
1567
|
};
|
|
1551
1568
|
}
|
|
1552
1569
|
const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
|
|
@@ -1566,30 +1583,158 @@ async function memTried(input, ctx) {
|
|
|
1566
1583
|
hint
|
|
1567
1584
|
};
|
|
1568
1585
|
}
|
|
1586
|
+
var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
|
|
1587
|
+
async function detectForAnchor(root, rel) {
|
|
1588
|
+
let dir = path7.resolve(root, rel);
|
|
1589
|
+
try {
|
|
1590
|
+
if (!statSync(dir).isDirectory()) dir = path7.dirname(dir);
|
|
1591
|
+
} catch {
|
|
1592
|
+
if (path7.extname(dir)) dir = path7.dirname(dir);
|
|
1593
|
+
}
|
|
1594
|
+
while (dir.startsWith(root)) {
|
|
1595
|
+
const pkgJson = path7.join(dir, "package.json");
|
|
1596
|
+
const hasPkg = existsSync17(pkgJson);
|
|
1597
|
+
const goMod = existsSync17(path7.join(dir, "go.mod"));
|
|
1598
|
+
const pySignal = PY_SIGNALS.some((s) => existsSync17(path7.join(dir, s)));
|
|
1599
|
+
if (hasPkg || goMod || pySignal) {
|
|
1600
|
+
let pkg = null;
|
|
1601
|
+
if (hasPkg) {
|
|
1602
|
+
try {
|
|
1603
|
+
pkg = JSON.parse(await readFile4(pkgJson, "utf8"));
|
|
1604
|
+
} catch {
|
|
1605
|
+
pkg = null;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
const baseDir = path7.relative(root, dir).split(path7.sep).join("/");
|
|
1609
|
+
return { framework: pickTestFramework(pkg, { goMod, pySignal }), baseDir };
|
|
1610
|
+
}
|
|
1611
|
+
const parent = path7.dirname(dir);
|
|
1612
|
+
if (parent === dir || dir === root) break;
|
|
1613
|
+
dir = parent;
|
|
1614
|
+
}
|
|
1615
|
+
return null;
|
|
1616
|
+
}
|
|
1617
|
+
async function detectTestFrameworkForPaths(root, anchorPaths) {
|
|
1618
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1619
|
+
for (const rel of starts) {
|
|
1620
|
+
const found = await detectForAnchor(root, rel);
|
|
1621
|
+
if (found) return found;
|
|
1622
|
+
}
|
|
1623
|
+
return { framework: "vitest", baseDir: "" };
|
|
1624
|
+
}
|
|
1625
|
+
async function detectTestFrameworksForAnchors(root, anchorPaths) {
|
|
1626
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1627
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1628
|
+
for (const rel of starts) {
|
|
1629
|
+
const found = await detectForAnchor(root, rel) ?? { framework: "vitest", baseDir: "" };
|
|
1630
|
+
const existing = groups.get(found.baseDir);
|
|
1631
|
+
if (existing) existing.anchors.push(rel);
|
|
1632
|
+
else groups.set(found.baseDir, { ...found, anchors: [rel] });
|
|
1633
|
+
}
|
|
1634
|
+
return [...groups.values()];
|
|
1635
|
+
}
|
|
1636
|
+
var ScaffoldTestInputSchema = {
|
|
1637
|
+
memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
|
|
1638
|
+
framework: z17.enum(["vitest", "jest", "pytest", "gotest"]).optional().describe("Test framework. Auto-detected from the package that owns the lesson's anchor paths when omitted."),
|
|
1639
|
+
out_path: z17.string().optional().describe("Override the generated test file path (repo-relative)."),
|
|
1640
|
+
write: z17.boolean().default(true).describe("Write the file to disk (default). false = return the content for preview without writing.")
|
|
1641
|
+
};
|
|
1642
|
+
async function scaffoldTest(input, ctx) {
|
|
1643
|
+
const loaded = existsSync17(ctx.paths.memoriesDir) ? await loadMemoriesFromDir14(ctx.paths.memoriesDir) : [];
|
|
1644
|
+
const found = loaded.find(({ memory }) => memory.frontmatter.id === input.memory_id);
|
|
1645
|
+
if (!found) {
|
|
1646
|
+
return { ok: false, error: `No memory found with id ${input.memory_id}`, memory_id: input.memory_id };
|
|
1647
|
+
}
|
|
1648
|
+
const anchorPaths = found.memory.frontmatter.anchor.paths ?? [];
|
|
1649
|
+
const allGroups = await detectTestFrameworksForAnchors(ctx.paths.root, anchorPaths);
|
|
1650
|
+
const groups = input.out_path ? allGroups.slice(0, 1) : allGroups;
|
|
1651
|
+
const frameworkFor = (detected) => input.framework ? normalizeFramework(input.framework) ?? detected : detected;
|
|
1652
|
+
const fields = parseLessonFields(found.memory.body);
|
|
1653
|
+
const lesson = {
|
|
1654
|
+
memoryId: input.memory_id,
|
|
1655
|
+
title: fields.title || input.memory_id,
|
|
1656
|
+
whyFailed: fields.whyFailed,
|
|
1657
|
+
instead: fields.instead,
|
|
1658
|
+
incident: found.memory.frontmatter.sensor?.incident,
|
|
1659
|
+
paths: anchorPaths
|
|
1660
|
+
};
|
|
1661
|
+
let scaffolds = groups.map(
|
|
1662
|
+
(g) => scaffoldPostIncidentTest(lesson, { framework: frameworkFor(g.framework), outPath: input.out_path, baseDir: g.baseDir })
|
|
1663
|
+
);
|
|
1664
|
+
let proposeCommand = scaffolds[0].proposeCommand;
|
|
1665
|
+
if (scaffolds.length > 1) {
|
|
1666
|
+
proposeCommand = buildProposeCommand(lesson, scaffolds.map((s) => s.runCommand).join(" && "));
|
|
1667
|
+
scaffolds = groups.map(
|
|
1668
|
+
(g) => scaffoldPostIncidentTest(lesson, {
|
|
1669
|
+
framework: frameworkFor(g.framework),
|
|
1670
|
+
baseDir: g.baseDir,
|
|
1671
|
+
proposeCommandOverride: proposeCommand
|
|
1672
|
+
})
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
const results = [];
|
|
1676
|
+
for (const scaffold of scaffolds) {
|
|
1677
|
+
const abs = path7.isAbsolute(scaffold.relPath) ? scaffold.relPath : path7.resolve(ctx.paths.root, scaffold.relPath);
|
|
1678
|
+
let written = false;
|
|
1679
|
+
let alreadyExists = false;
|
|
1680
|
+
if (input.write) {
|
|
1681
|
+
if (existsSync17(abs)) {
|
|
1682
|
+
alreadyExists = true;
|
|
1683
|
+
} else {
|
|
1684
|
+
await mkdir4(path7.dirname(abs), { recursive: true });
|
|
1685
|
+
await writeFile10(abs, scaffold.content, "utf8");
|
|
1686
|
+
written = true;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
results.push({
|
|
1690
|
+
framework: scaffold.framework,
|
|
1691
|
+
path: scaffold.relPath,
|
|
1692
|
+
run_command: scaffold.runCommand,
|
|
1693
|
+
content: scaffold.content,
|
|
1694
|
+
written,
|
|
1695
|
+
already_exists: alreadyExists
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
const first = results[0];
|
|
1699
|
+
const anyExisting = results.some((r) => r.already_exists);
|
|
1700
|
+
return {
|
|
1701
|
+
ok: true,
|
|
1702
|
+
memory_id: input.memory_id,
|
|
1703
|
+
framework: first.framework,
|
|
1704
|
+
path: first.path,
|
|
1705
|
+
run_command: first.run_command,
|
|
1706
|
+
propose_command: proposeCommand,
|
|
1707
|
+
content: first.content,
|
|
1708
|
+
written: first.written,
|
|
1709
|
+
already_exists: first.already_exists,
|
|
1710
|
+
...results.length > 1 ? { scaffolds: results } : {},
|
|
1711
|
+
notice: (results.length > 1 ? `Lesson spans ${results.length} packages \u2014 one pending test per owning package; ONE propose_command arms them all (chained oracle). ` : "") + (anyExisting ? "Some file(s) already exist \u2014 not overwritten. Delete them or pass out_path to write elsewhere." : "PENDING test scaffolded. Fill in the assertion (RED on the incident, GREEN once fixed), run it, then arm it with propose_command \u2014 propose_sensor stays the sole validated writer.")
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1569
1714
|
var IngestFindingsInputSchema = {
|
|
1570
|
-
format:
|
|
1571
|
-
report_path:
|
|
1572
|
-
report:
|
|
1573
|
-
type:
|
|
1574
|
-
scope:
|
|
1575
|
-
module:
|
|
1576
|
-
min_severity:
|
|
1577
|
-
include_stylistic:
|
|
1578
|
-
limit:
|
|
1579
|
-
author:
|
|
1580
|
-
dry_run:
|
|
1715
|
+
format: z18.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
|
|
1716
|
+
report_path: z18.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
|
|
1717
|
+
report: z18.string().optional().describe("Inline findings JSON content. Provide this OR `report_path`."),
|
|
1718
|
+
type: z18.enum(["gotcha", "convention"]).default("gotcha").describe("Memory type for the created drafts"),
|
|
1719
|
+
scope: z18.enum(["personal", "team", "module"]).default("team").describe("Visibility scope for the created memories"),
|
|
1720
|
+
module: z18.string().optional().describe("Module name (required when scope=module)"),
|
|
1721
|
+
min_severity: z18.enum(["info", "minor", "major", "critical", "blocker"]).optional().describe("Ignore findings below this severity"),
|
|
1722
|
+
include_stylistic: z18.boolean().optional().describe("Also ingest auto-fixable stylistic rules (semi/quotes/prefer-const\u2026); off by default as low-value noise"),
|
|
1723
|
+
limit: z18.number().int().positive().optional().describe("Cap the number of memories created"),
|
|
1724
|
+
author: z18.string().optional().describe("Author handle or email"),
|
|
1725
|
+
dry_run: z18.boolean().default(false).describe("When true, return the drafts that WOULD be created without writing them")
|
|
1581
1726
|
};
|
|
1582
1727
|
async function ingestFindings(input, ctx) {
|
|
1583
|
-
if (!
|
|
1728
|
+
if (!existsSync18(ctx.paths.haiveDir)) {
|
|
1584
1729
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1585
1730
|
}
|
|
1586
1731
|
let raw;
|
|
1587
1732
|
if (input.report && input.report.trim()) {
|
|
1588
1733
|
raw = input.report;
|
|
1589
1734
|
} else if (input.report_path) {
|
|
1590
|
-
const file =
|
|
1591
|
-
if (!
|
|
1592
|
-
raw = await
|
|
1735
|
+
const file = path8.resolve(ctx.paths.root, input.report_path);
|
|
1736
|
+
if (!existsSync18(file)) throw new Error(`Report file not found: ${file}`);
|
|
1737
|
+
raw = await readFile5(file, "utf8");
|
|
1593
1738
|
} else {
|
|
1594
1739
|
throw new Error("Provide either `report_path` or `report`.");
|
|
1595
1740
|
}
|
|
@@ -1603,7 +1748,7 @@ async function ingestFindings(input, ctx) {
|
|
|
1603
1748
|
...input.include_stylistic ? { includeStylistic: true } : {},
|
|
1604
1749
|
...input.limit ? { limit: input.limit } : {}
|
|
1605
1750
|
});
|
|
1606
|
-
const existing =
|
|
1751
|
+
const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir15(ctx.paths.memoriesDir) : [];
|
|
1607
1752
|
const existingTopics = new Set(
|
|
1608
1753
|
existing.map(({ memory }) => memory.frontmatter.topic).filter((t) => Boolean(t))
|
|
1609
1754
|
);
|
|
@@ -1641,12 +1786,12 @@ async function writeDraft(ctx, draft) {
|
|
|
1641
1786
|
draft.frontmatter.id,
|
|
1642
1787
|
draft.frontmatter.module
|
|
1643
1788
|
);
|
|
1644
|
-
await
|
|
1645
|
-
await
|
|
1789
|
+
await mkdir5(path8.dirname(file), { recursive: true });
|
|
1790
|
+
await writeFile11(file, serializeMemory9({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
1646
1791
|
return file;
|
|
1647
1792
|
}
|
|
1648
1793
|
function pendingDistillPath(ctx) {
|
|
1649
|
-
return
|
|
1794
|
+
return path9.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
|
|
1650
1795
|
}
|
|
1651
1796
|
var SessionTracker = class {
|
|
1652
1797
|
events = [];
|
|
@@ -1750,7 +1895,7 @@ var SessionTracker = class {
|
|
|
1750
1895
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
1751
1896
|
);
|
|
1752
1897
|
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
1753
|
-
if (!ranPostTask && isSubstantialSession &&
|
|
1898
|
+
if (!ranPostTask && isSubstantialSession && existsSync19(this.ctx.paths.haiveDir)) {
|
|
1754
1899
|
try {
|
|
1755
1900
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
1756
1901
|
const payload = {
|
|
@@ -1763,9 +1908,9 @@ var SessionTracker = class {
|
|
|
1763
1908
|
...gitDiff ? { git_diff: gitDiff } : {},
|
|
1764
1909
|
...recapId ? { recap_id: recapId } : {}
|
|
1765
1910
|
};
|
|
1766
|
-
const cacheDir =
|
|
1767
|
-
await
|
|
1768
|
-
await
|
|
1911
|
+
const cacheDir = path9.join(this.ctx.paths.haiveDir, ".cache");
|
|
1912
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
1913
|
+
await writeFile12(
|
|
1769
1914
|
pendingDistillPath(this.ctx),
|
|
1770
1915
|
JSON.stringify(payload, null, 2) + "\n",
|
|
1771
1916
|
"utf8"
|
|
@@ -1784,7 +1929,7 @@ var SessionTracker = class {
|
|
|
1784
1929
|
};
|
|
1785
1930
|
async function clearPendingDistill(ctx) {
|
|
1786
1931
|
const p = pendingDistillPath(ctx);
|
|
1787
|
-
if (
|
|
1932
|
+
if (existsSync19(p)) {
|
|
1788
1933
|
try {
|
|
1789
1934
|
await rm(p);
|
|
1790
1935
|
} catch {
|
|
@@ -1799,15 +1944,15 @@ function summarizeTools(events) {
|
|
|
1799
1944
|
return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} \xD7${n}`).join(", ");
|
|
1800
1945
|
}
|
|
1801
1946
|
var MemSessionEndInputSchema = {
|
|
1802
|
-
goal:
|
|
1803
|
-
accomplished:
|
|
1804
|
-
discoveries:
|
|
1947
|
+
goal: z19.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1948
|
+
accomplished: z19.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1949
|
+
discoveries: z19.string().default("").describe(
|
|
1805
1950
|
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1806
1951
|
),
|
|
1807
|
-
files_touched:
|
|
1808
|
-
next_steps:
|
|
1809
|
-
scope:
|
|
1810
|
-
module:
|
|
1952
|
+
files_touched: z19.array(z19.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1953
|
+
next_steps: z19.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1954
|
+
scope: z19.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1955
|
+
module: z19.string().optional().describe("Module name (required when scope=module)")
|
|
1811
1956
|
};
|
|
1812
1957
|
function recapTopic(scope, module) {
|
|
1813
1958
|
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
@@ -1837,23 +1982,23 @@ ${input.next_steps}`);
|
|
|
1837
1982
|
return lines.join("\n");
|
|
1838
1983
|
}
|
|
1839
1984
|
async function memSessionEnd(input, ctx) {
|
|
1840
|
-
if (!
|
|
1985
|
+
if (!existsSync20(ctx.paths.haiveDir)) {
|
|
1841
1986
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1842
1987
|
}
|
|
1843
1988
|
const body = buildBody(input);
|
|
1844
1989
|
const topic = recapTopic(input.scope, input.module);
|
|
1845
1990
|
const normalizedFiles = input.files_touched.map((p) => {
|
|
1846
|
-
if (!p || !
|
|
1847
|
-
const rel =
|
|
1991
|
+
if (!p || !path10.isAbsolute(p)) return p;
|
|
1992
|
+
const rel = path10.relative(ctx.paths.root, p);
|
|
1848
1993
|
return rel.startsWith("..") ? p : rel;
|
|
1849
1994
|
});
|
|
1850
1995
|
const invalidPaths = normalizedFiles.filter(
|
|
1851
|
-
(p) => !
|
|
1996
|
+
(p) => !existsSync20(path10.resolve(ctx.paths.root, p))
|
|
1852
1997
|
);
|
|
1853
1998
|
if (invalidPaths.length > 0) {
|
|
1854
1999
|
console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
|
|
1855
2000
|
}
|
|
1856
|
-
const existing =
|
|
2001
|
+
const existing = existsSync20(ctx.paths.memoriesDir) ? await loadMemoriesFromDir16(ctx.paths.memoriesDir) : [];
|
|
1857
2002
|
const topicMatch = existing.find(
|
|
1858
2003
|
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1859
2004
|
);
|
|
@@ -1869,7 +2014,7 @@ async function memSessionEnd(input, ctx) {
|
|
|
1869
2014
|
paths: normalizedFiles.length ? normalizedFiles : fm.anchor.paths
|
|
1870
2015
|
}
|
|
1871
2016
|
};
|
|
1872
|
-
await
|
|
2017
|
+
await writeFile13(
|
|
1873
2018
|
topicMatch.filePath,
|
|
1874
2019
|
serializeMemory10({ frontmatter: newFrontmatter, body }),
|
|
1875
2020
|
"utf8"
|
|
@@ -1899,8 +2044,8 @@ async function memSessionEnd(input, ctx) {
|
|
|
1899
2044
|
frontmatter.id,
|
|
1900
2045
|
frontmatter.module
|
|
1901
2046
|
);
|
|
1902
|
-
await
|
|
1903
|
-
await
|
|
2047
|
+
await mkdir7(path10.dirname(file), { recursive: true });
|
|
2048
|
+
await writeFile13(file, serializeMemory10({ frontmatter, body }), "utf8");
|
|
1904
2049
|
await clearPendingDistill(ctx);
|
|
1905
2050
|
return {
|
|
1906
2051
|
id: frontmatter.id,
|
|
@@ -2036,53 +2181,53 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
2036
2181
|
}
|
|
2037
2182
|
async function loadModuleContexts2(ctx, modules) {
|
|
2038
2183
|
if (modules.length === 0) return [];
|
|
2039
|
-
if (!
|
|
2184
|
+
if (!existsSync21(ctx.paths.modulesContextDir)) return [];
|
|
2040
2185
|
const available = new Set(
|
|
2041
2186
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
2042
2187
|
);
|
|
2043
2188
|
const out = [];
|
|
2044
2189
|
for (const m of modules) {
|
|
2045
2190
|
if (!available.has(m)) continue;
|
|
2046
|
-
const file =
|
|
2047
|
-
if (
|
|
2048
|
-
out.push({ name: m, content: await
|
|
2191
|
+
const file = path11.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
2192
|
+
if (existsSync21(file)) {
|
|
2193
|
+
out.push({ name: m, content: await readFile6(file, "utf8") });
|
|
2049
2194
|
}
|
|
2050
2195
|
}
|
|
2051
2196
|
return out;
|
|
2052
2197
|
}
|
|
2053
2198
|
var GetBriefingInputSchema = {
|
|
2054
|
-
task:
|
|
2199
|
+
task: z20.string().optional().describe(
|
|
2055
2200
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
2056
2201
|
),
|
|
2057
|
-
files:
|
|
2058
|
-
max_tokens:
|
|
2202
|
+
files: z20.array(z20.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
2203
|
+
max_tokens: z20.number().int().positive().default(8e3).describe(
|
|
2059
2204
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
2060
2205
|
),
|
|
2061
|
-
max_memories:
|
|
2062
|
-
include_project_context:
|
|
2063
|
-
dedupe_project_context:
|
|
2206
|
+
max_memories: z20.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
2207
|
+
include_project_context: z20.boolean().default(true),
|
|
2208
|
+
dedupe_project_context: z20.boolean().optional().describe(
|
|
2064
2209
|
"Token saver (default ON): skip re-emitting the project-context body if an identical copy was already sent within the last few minutes this session (the agent still has it). Set false to always include it."
|
|
2065
2210
|
),
|
|
2066
|
-
include_module_contexts:
|
|
2067
|
-
semantic:
|
|
2211
|
+
include_module_contexts: z20.boolean().default(true),
|
|
2212
|
+
semantic: z20.boolean().default(true).describe(
|
|
2068
2213
|
"Use semantic ranking when a task is provided (requires `hivelore embeddings index`)."
|
|
2069
2214
|
),
|
|
2070
|
-
include_stale:
|
|
2071
|
-
track:
|
|
2072
|
-
format:
|
|
2215
|
+
include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
2216
|
+
track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
2217
|
+
format: z20.enum(["full", "compact", "actions"]).default("full").describe(
|
|
2073
2218
|
"Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
|
|
2074
2219
|
),
|
|
2075
|
-
symbols:
|
|
2220
|
+
symbols: z20.array(z20.string()).default([]).describe(
|
|
2076
2221
|
"Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `hivelore index code` to have been run."
|
|
2077
2222
|
),
|
|
2078
|
-
min_semantic_score:
|
|
2223
|
+
min_semantic_score: z20.number().min(0).max(1).default(0).describe(
|
|
2079
2224
|
"Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
|
|
2080
2225
|
),
|
|
2081
|
-
budget_preset:
|
|
2226
|
+
budget_preset: z20.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
2082
2227
|
"Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
|
|
2083
2228
|
)
|
|
2084
2229
|
};
|
|
2085
|
-
var GetBriefingZod =
|
|
2230
|
+
var GetBriefingZod = z20.object(GetBriefingInputSchema);
|
|
2086
2231
|
async function getBriefing(input, ctx) {
|
|
2087
2232
|
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
2088
2233
|
max_tokens: input.max_tokens,
|
|
@@ -2098,8 +2243,8 @@ async function getBriefing(input, ctx) {
|
|
|
2098
2243
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
2099
2244
|
let byId = /* @__PURE__ */ new Map();
|
|
2100
2245
|
let lastSession;
|
|
2101
|
-
if (
|
|
2102
|
-
const allLoaded = await
|
|
2246
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2247
|
+
const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2103
2248
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
2104
2249
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2105
2250
|
);
|
|
@@ -2273,7 +2418,7 @@ async function getBriefing(input, ctx) {
|
|
|
2273
2418
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
2274
2419
|
const newFm = { ...loaded.memory.frontmatter, status: "validated", validated_by: "auto" };
|
|
2275
2420
|
try {
|
|
2276
|
-
await
|
|
2421
|
+
await writeFile14(loaded.filePath, serializeMemory11({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
|
|
2277
2422
|
m.status = "validated";
|
|
2278
2423
|
m.confidence = "trusted";
|
|
2279
2424
|
} catch {
|
|
@@ -2281,7 +2426,7 @@ async function getBriefing(input, ctx) {
|
|
|
2281
2426
|
}
|
|
2282
2427
|
}
|
|
2283
2428
|
}
|
|
2284
|
-
let projectContextRaw = input.include_project_context &&
|
|
2429
|
+
let projectContextRaw = input.include_project_context && existsSync22(ctx.paths.projectContext) ? await readFile7(ctx.paths.projectContext, "utf8") : "";
|
|
2285
2430
|
let contextOmittedRecent = false;
|
|
2286
2431
|
if (projectContextRaw && input.dedupe_project_context !== false) {
|
|
2287
2432
|
const ctxHash = hashProjectContext(projectContextRaw);
|
|
@@ -2296,7 +2441,7 @@ async function getBriefing(input, ctx) {
|
|
|
2296
2441
|
const setupWarnings = [];
|
|
2297
2442
|
let autoContextGenerated = false;
|
|
2298
2443
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
2299
|
-
if ((isTemplateContext || !
|
|
2444
|
+
if ((isTemplateContext || !existsSync22(ctx.paths.projectContext)) && input.include_project_context) {
|
|
2300
2445
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
2301
2446
|
if (haiveConfig.autoContext) {
|
|
2302
2447
|
const codeMap = await loadCodeMap(ctx.paths);
|
|
@@ -2470,8 +2615,8 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2470
2615
|
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
2471
2616
|
}
|
|
2472
2617
|
}
|
|
2473
|
-
if (
|
|
2474
|
-
const allMems = await
|
|
2618
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2619
|
+
const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2475
2620
|
for (const { memory } of allMems) {
|
|
2476
2621
|
const fm = memory.frontmatter;
|
|
2477
2622
|
if (!fm.requires_human_approval) continue;
|
|
@@ -2481,9 +2626,9 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2481
2626
|
}
|
|
2482
2627
|
}
|
|
2483
2628
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
2484
|
-
if (
|
|
2629
|
+
if (existsSync22(pendingDistillFile)) {
|
|
2485
2630
|
try {
|
|
2486
|
-
const raw = await
|
|
2631
|
+
const raw = await readFile7(pendingDistillFile, "utf8");
|
|
2487
2632
|
const pd = JSON.parse(raw);
|
|
2488
2633
|
const ageMs = Date.now() - new Date(pd.session_end).getTime();
|
|
2489
2634
|
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -2510,7 +2655,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2510
2655
|
}
|
|
2511
2656
|
}
|
|
2512
2657
|
const memoriesEmpty = outputMemories.length === 0;
|
|
2513
|
-
const hasMemoriesDir =
|
|
2658
|
+
const hasMemoriesDir = existsSync22(ctx.paths.memoriesDir);
|
|
2514
2659
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
2515
2660
|
const hasUnguessableSignal = outputMemories.some(
|
|
2516
2661
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -2525,10 +2670,10 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2525
2670
|
try {
|
|
2526
2671
|
let pcRaw = "";
|
|
2527
2672
|
try {
|
|
2528
|
-
pcRaw = await
|
|
2673
|
+
pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
|
|
2529
2674
|
} catch {
|
|
2530
2675
|
}
|
|
2531
|
-
const allForBootstrap =
|
|
2676
|
+
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
|
|
2532
2677
|
const cmForBootstrap = await loadCodeMap(ctx.paths);
|
|
2533
2678
|
let existingModules = [];
|
|
2534
2679
|
try {
|
|
@@ -2592,7 +2737,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2592
2737
|
"No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
|
|
2593
2738
|
);
|
|
2594
2739
|
}
|
|
2595
|
-
if (outputMemories.length > 0 &&
|
|
2740
|
+
if (outputMemories.length > 0 && existsSync22(ctx.paths.haiveDir)) {
|
|
2596
2741
|
const proof = briefingProofLine(await loadPreventionEvents(ctx.paths));
|
|
2597
2742
|
if (proof) hints.push(proof);
|
|
2598
2743
|
}
|
|
@@ -2606,7 +2751,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2606
2751
|
adaptiveTrim
|
|
2607
2752
|
});
|
|
2608
2753
|
const breadcrumbTokens = breadcrumbs ? estimateTokens([...breadcrumbs.start_here, ...breadcrumbs.drill_down, breadcrumbs.note ?? ""].join("\n")) : 0;
|
|
2609
|
-
if (
|
|
2754
|
+
if (existsSync22(ctx.paths.haiveDir)) {
|
|
2610
2755
|
await writeBriefingMarker(ctx.paths, {
|
|
2611
2756
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
2612
2757
|
...input.task ? { task: input.task } : {},
|
|
@@ -2662,10 +2807,10 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2662
2807
|
};
|
|
2663
2808
|
}
|
|
2664
2809
|
async function detectRunCommands(root) {
|
|
2665
|
-
const pkgPath =
|
|
2666
|
-
if (!
|
|
2810
|
+
const pkgPath = path12.join(root, "package.json");
|
|
2811
|
+
if (!existsSync22(pkgPath)) return null;
|
|
2667
2812
|
try {
|
|
2668
|
-
const pkg = JSON.parse(await
|
|
2813
|
+
const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
|
|
2669
2814
|
const scripts = pkg.scripts ?? {};
|
|
2670
2815
|
const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
|
|
2671
2816
|
const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
|
|
@@ -2730,20 +2875,20 @@ function oneLine(value) {
|
|
|
2730
2875
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
2731
2876
|
}
|
|
2732
2877
|
function serverVersion() {
|
|
2733
|
-
return true ? "0.
|
|
2878
|
+
return true ? "0.39.0" : "dev";
|
|
2734
2879
|
}
|
|
2735
2880
|
var CodeMapInputSchema = {
|
|
2736
|
-
file:
|
|
2737
|
-
symbol:
|
|
2738
|
-
paths:
|
|
2881
|
+
file: z21.string().optional().describe("Filter to files whose path contains this substring"),
|
|
2882
|
+
symbol: z21.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
2883
|
+
paths: z21.array(z21.string()).default([]).describe(
|
|
2739
2884
|
"Filter to files under any of these path prefixes (e.g. ['packages/mcp/src/tools/', 'src/auth/']). OR-joined with `file` substring; useful to get a focused view of one module."
|
|
2740
2885
|
),
|
|
2741
|
-
max_files:
|
|
2742
|
-
max_tokens:
|
|
2886
|
+
max_files: z21.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
|
|
2887
|
+
max_tokens: z21.number().int().positive().optional().describe(
|
|
2743
2888
|
"Approximate token budget for the response. When the matching set exceeds it, files are ranked by export density (exports per LOC) and the highest-signal ones are kept first. Omit to disable budgeting (legacy behavior)."
|
|
2744
2889
|
)
|
|
2745
2890
|
};
|
|
2746
|
-
var CodeMapInputZod =
|
|
2891
|
+
var CodeMapInputZod = z21.object(CodeMapInputSchema);
|
|
2747
2892
|
async function codeMapTool(input, ctx) {
|
|
2748
2893
|
const map = await loadCodeMap2(ctx.paths);
|
|
2749
2894
|
if (!map) {
|
|
@@ -2811,14 +2956,14 @@ function estimateFileEntryTokens(f) {
|
|
|
2811
2956
|
return estimateTokens2(f.path) + estimateTokens2(f.entry.summary ?? "") + exportsCost + 4;
|
|
2812
2957
|
}
|
|
2813
2958
|
var MemDiffInputSchema = {
|
|
2814
|
-
id_a:
|
|
2815
|
-
id_b:
|
|
2959
|
+
id_a: z22.string().min(1).describe("First memory id"),
|
|
2960
|
+
id_b: z22.string().min(1).describe("Second memory id")
|
|
2816
2961
|
};
|
|
2817
2962
|
async function memDiff(input, ctx) {
|
|
2818
|
-
if (!
|
|
2963
|
+
if (!existsSync23(ctx.paths.memoriesDir)) {
|
|
2819
2964
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
2820
2965
|
}
|
|
2821
|
-
const all = await
|
|
2966
|
+
const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
|
|
2822
2967
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
2823
2968
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
2824
2969
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -2851,15 +2996,15 @@ async function memDiff(input, ctx) {
|
|
|
2851
2996
|
};
|
|
2852
2997
|
}
|
|
2853
2998
|
var GetRecapInputSchema = {
|
|
2854
|
-
scope:
|
|
2999
|
+
scope: z23.enum(["personal", "team", "any"]).default("any").describe(
|
|
2855
3000
|
"Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
|
|
2856
3001
|
)
|
|
2857
3002
|
};
|
|
2858
3003
|
async function getRecap(input, ctx) {
|
|
2859
|
-
if (!
|
|
3004
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
2860
3005
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
2861
3006
|
}
|
|
2862
|
-
const all = await
|
|
3007
|
+
const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
|
|
2863
3008
|
const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
|
|
2864
3009
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2865
3010
|
);
|
|
@@ -2882,11 +3027,11 @@ async function getRecap(input, ctx) {
|
|
|
2882
3027
|
};
|
|
2883
3028
|
}
|
|
2884
3029
|
var MemRelevantToInputSchema = {
|
|
2885
|
-
task:
|
|
2886
|
-
files:
|
|
2887
|
-
limit:
|
|
2888
|
-
min_semantic_score:
|
|
2889
|
-
format:
|
|
3030
|
+
task: z24.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
|
|
3031
|
+
files: z24.array(z24.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
3032
|
+
limit: z24.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
3033
|
+
min_semantic_score: z24.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
3034
|
+
format: z24.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
2890
3035
|
};
|
|
2891
3036
|
async function memRelevantTo(input, ctx) {
|
|
2892
3037
|
const briefingInput = {
|
|
@@ -2915,11 +3060,11 @@ async function memRelevantTo(input, ctx) {
|
|
|
2915
3060
|
return out;
|
|
2916
3061
|
}
|
|
2917
3062
|
var CodeSearchInputSchema = {
|
|
2918
|
-
query:
|
|
3063
|
+
query: z25.string().min(1).describe(
|
|
2919
3064
|
"Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
|
|
2920
3065
|
),
|
|
2921
|
-
k:
|
|
2922
|
-
min_score:
|
|
3066
|
+
k: z25.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
|
|
3067
|
+
min_score: z25.number().min(0).max(1).default(0.2).describe(
|
|
2923
3068
|
"Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
|
|
2924
3069
|
)
|
|
2925
3070
|
};
|
|
@@ -2961,17 +3106,17 @@ async function codeSearch(input, ctx) {
|
|
|
2961
3106
|
};
|
|
2962
3107
|
}
|
|
2963
3108
|
var AntiPatternsCheckInputSchema = {
|
|
2964
|
-
diff:
|
|
3109
|
+
diff: z26.string().optional().describe(
|
|
2965
3110
|
"Raw unified diff text (or any code/text snippet) to scan for previously documented anti-patterns. Tokens from the diff are used to match memory bodies and the embeddings index."
|
|
2966
3111
|
),
|
|
2967
|
-
paths:
|
|
3112
|
+
paths: z26.array(z26.string()).default([]).describe(
|
|
2968
3113
|
"File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
|
|
2969
3114
|
),
|
|
2970
|
-
limit:
|
|
2971
|
-
semantic:
|
|
3115
|
+
limit: z26.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
|
|
3116
|
+
semantic: z26.boolean().default(true).describe(
|
|
2972
3117
|
"When true, also use semantic search (requires @hivelore/embeddings + memory index) to find related anti-patterns."
|
|
2973
3118
|
),
|
|
2974
|
-
min_semantic_score:
|
|
3119
|
+
min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
|
|
2975
3120
|
"Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
|
|
2976
3121
|
)
|
|
2977
3122
|
};
|
|
@@ -3055,10 +3200,10 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3055
3200
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
3056
3201
|
};
|
|
3057
3202
|
}
|
|
3058
|
-
if (!
|
|
3203
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
3059
3204
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
3060
3205
|
}
|
|
3061
|
-
const all = await
|
|
3206
|
+
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
3062
3207
|
const minSemanticScore = input.min_semantic_score ?? 0.45;
|
|
3063
3208
|
const negative = all.filter(({ memory }) => {
|
|
3064
3209
|
const t = memory.frontmatter.type;
|
|
@@ -3173,12 +3318,12 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3173
3318
|
};
|
|
3174
3319
|
}
|
|
3175
3320
|
var MemDistillInputSchema = {
|
|
3176
|
-
since_days:
|
|
3177
|
-
min_cluster:
|
|
3178
|
-
type_filter:
|
|
3321
|
+
since_days: z27.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
|
|
3322
|
+
min_cluster: z27.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
|
|
3323
|
+
type_filter: z27.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
|
|
3179
3324
|
"Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
|
|
3180
3325
|
),
|
|
3181
|
-
scope:
|
|
3326
|
+
scope: z27.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
|
|
3182
3327
|
};
|
|
3183
3328
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
3184
3329
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -3218,11 +3363,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
3218
3363
|
"error"
|
|
3219
3364
|
]);
|
|
3220
3365
|
async function memDistill(input, ctx) {
|
|
3221
|
-
if (!
|
|
3366
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
3222
3367
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
3223
3368
|
}
|
|
3224
3369
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
3225
|
-
const all = await
|
|
3370
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
3226
3371
|
const candidates = all.filter(({ memory }) => {
|
|
3227
3372
|
const fm = memory.frontmatter;
|
|
3228
3373
|
if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
|
|
@@ -3325,15 +3470,15 @@ function firstHeading(body) {
|
|
|
3325
3470
|
return void 0;
|
|
3326
3471
|
}
|
|
3327
3472
|
var PreCommitCheckInputSchema = {
|
|
3328
|
-
diff:
|
|
3473
|
+
diff: z28.string().optional().describe(
|
|
3329
3474
|
"Raw unified diff text to scan. If omitted, only `paths` is used. When called from a pre-commit hook, pipe the output of `git diff --cached`."
|
|
3330
3475
|
),
|
|
3331
|
-
paths:
|
|
3332
|
-
block_on:
|
|
3476
|
+
paths: z28.array(z28.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
|
|
3477
|
+
block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
|
|
3333
3478
|
"When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
|
|
3334
3479
|
),
|
|
3335
|
-
semantic:
|
|
3336
|
-
anchored_blocks:
|
|
3480
|
+
semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
|
|
3481
|
+
anchored_blocks: z28.boolean().default(false).describe(
|
|
3337
3482
|
"When true, ALSO block a high-confidence anti-pattern (attempt/gotcha) that is anchored to a touched file AND corroborated by the diff (literal token overlap, or semantic >= 0.45) \u2014 not just very strong semantic matches. Powers the 'anchored' enforcement gate. Config/docs-only commits are still downgraded. Default false preserves the soft, semantic-only blocking behavior."
|
|
3338
3483
|
)
|
|
3339
3484
|
};
|
|
@@ -3650,17 +3795,17 @@ function suggestResolution(byId, idA, idB) {
|
|
|
3650
3795
|
};
|
|
3651
3796
|
}
|
|
3652
3797
|
var MemConflictCandidatesInputSchema = {
|
|
3653
|
-
since_days:
|
|
3654
|
-
types:
|
|
3655
|
-
min_jaccard:
|
|
3656
|
-
max_pairs:
|
|
3657
|
-
max_scan:
|
|
3658
|
-
max_topic_pairs:
|
|
3798
|
+
since_days: z29.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
3799
|
+
types: z29.array(z29.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
3800
|
+
min_jaccard: z29.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
3801
|
+
max_pairs: z29.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
3802
|
+
max_scan: z29.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
|
|
3803
|
+
max_topic_pairs: z29.number().int().positive().max(100).default(20).describe(
|
|
3659
3804
|
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
3660
3805
|
)
|
|
3661
3806
|
};
|
|
3662
3807
|
async function memConflictCandidates(input, ctx) {
|
|
3663
|
-
if (!
|
|
3808
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
3664
3809
|
return {
|
|
3665
3810
|
pairs: [],
|
|
3666
3811
|
topic_status_pairs: [],
|
|
@@ -3669,7 +3814,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3669
3814
|
notice: "No .ai/memories directory."
|
|
3670
3815
|
};
|
|
3671
3816
|
}
|
|
3672
|
-
const all = await
|
|
3817
|
+
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
3673
3818
|
const byId = new Map(all.map((m) => [m.memory.frontmatter.id, m]));
|
|
3674
3819
|
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
3675
3820
|
sinceDays: input.since_days,
|
|
@@ -3697,7 +3842,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3697
3842
|
};
|
|
3698
3843
|
}
|
|
3699
3844
|
var MemResolveProjectInputSchema = {
|
|
3700
|
-
cwd:
|
|
3845
|
+
cwd: z30.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
3701
3846
|
};
|
|
3702
3847
|
async function memResolveProject(input, _ctx) {
|
|
3703
3848
|
void _ctx;
|
|
@@ -3710,7 +3855,7 @@ async function memResolveProject(input, _ctx) {
|
|
|
3710
3855
|
}
|
|
3711
3856
|
var MemSuggestTopicInputSchema = {
|
|
3712
3857
|
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
3713
|
-
title:
|
|
3858
|
+
title: z31.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
3714
3859
|
};
|
|
3715
3860
|
async function memSuggestTopic(input, _ctx) {
|
|
3716
3861
|
void _ctx;
|
|
@@ -3718,15 +3863,15 @@ async function memSuggestTopic(input, _ctx) {
|
|
|
3718
3863
|
return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
|
|
3719
3864
|
}
|
|
3720
3865
|
var MemTimelineInputSchema = {
|
|
3721
|
-
memory_id:
|
|
3722
|
-
topic:
|
|
3723
|
-
limit:
|
|
3866
|
+
memory_id: z32.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
3867
|
+
topic: z32.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
3868
|
+
limit: z32.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3724
3869
|
};
|
|
3725
3870
|
async function memTimeline(input, ctx) {
|
|
3726
|
-
if (!
|
|
3871
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
3727
3872
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3728
3873
|
}
|
|
3729
|
-
const all = await
|
|
3874
|
+
const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
|
|
3730
3875
|
const { entries, notice } = collectTimelineEntries(all, {
|
|
3731
3876
|
memoryId: input.memory_id,
|
|
3732
3877
|
topic: input.topic,
|
|
@@ -3735,10 +3880,10 @@ async function memTimeline(input, ctx) {
|
|
|
3735
3880
|
return { entries, total: entries.length, notice };
|
|
3736
3881
|
}
|
|
3737
3882
|
var BootstrapProjectArgsSchema = {
|
|
3738
|
-
module:
|
|
3883
|
+
module: z33.string().optional().describe(
|
|
3739
3884
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
3740
3885
|
),
|
|
3741
|
-
focus:
|
|
3886
|
+
focus: z33.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
3742
3887
|
};
|
|
3743
3888
|
var ROOT_TEMPLATE = `# Project context
|
|
3744
3889
|
|
|
@@ -3819,15 +3964,15 @@ ${template}\`\`\`
|
|
|
3819
3964
|
};
|
|
3820
3965
|
}
|
|
3821
3966
|
var BootstrapRepoArgsSchema = {
|
|
3822
|
-
focus:
|
|
3967
|
+
focus: z34.string().optional().describe("Optional area to emphasize first (e.g. 'payments', 'auth').")
|
|
3823
3968
|
};
|
|
3824
3969
|
async function currentAssessment(ctx) {
|
|
3825
3970
|
let projectContextRaw = "";
|
|
3826
3971
|
try {
|
|
3827
|
-
projectContextRaw = await
|
|
3972
|
+
projectContextRaw = await readFile8(ctx.paths.projectContext, "utf8");
|
|
3828
3973
|
} catch {
|
|
3829
3974
|
}
|
|
3830
|
-
const memories =
|
|
3975
|
+
const memories = existsSync29(ctx.paths.memoriesDir) ? await loadMemoriesFromDir24(ctx.paths.memoriesDir) : [];
|
|
3831
3976
|
const codeMap = await loadCodeMap4(ctx.paths);
|
|
3832
3977
|
let existingModules = [];
|
|
3833
3978
|
try {
|
|
@@ -3894,8 +4039,8 @@ Main code areas detected: ${areas}
|
|
|
3894
4039
|
};
|
|
3895
4040
|
}
|
|
3896
4041
|
var PostTaskArgsSchema = {
|
|
3897
|
-
task_summary:
|
|
3898
|
-
files_touched:
|
|
4042
|
+
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
4043
|
+
files_touched: z35.array(z35.string()).optional().describe("Files you created or modified during the task")
|
|
3899
4044
|
};
|
|
3900
4045
|
function postTaskPrompt(args, ctx) {
|
|
3901
4046
|
const taskLine = args.task_summary ? `
|
|
@@ -3991,10 +4136,10 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
3991
4136
|
};
|
|
3992
4137
|
}
|
|
3993
4138
|
var ImportDocsArgsSchema = {
|
|
3994
|
-
content:
|
|
3995
|
-
source:
|
|
3996
|
-
scope:
|
|
3997
|
-
dry_run:
|
|
4139
|
+
content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
4140
|
+
source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
4141
|
+
scope: z36.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
4142
|
+
dry_run: z36.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
3998
4143
|
};
|
|
3999
4144
|
function importDocsPrompt(args, ctx) {
|
|
4000
4145
|
const sourceLine = args.source ? `
|
|
@@ -4057,7 +4202,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4057
4202
|
};
|
|
4058
4203
|
}
|
|
4059
4204
|
var SERVER_NAME = "hivelore";
|
|
4060
|
-
var SERVER_VERSION = "0.
|
|
4205
|
+
var SERVER_VERSION = "0.39.0";
|
|
4061
4206
|
function jsonResult(data) {
|
|
4062
4207
|
return {
|
|
4063
4208
|
content: [
|
|
@@ -4080,7 +4225,8 @@ var ENFORCEMENT_PROFILE_TOOLS = [
|
|
|
4080
4225
|
"code_search",
|
|
4081
4226
|
"pre_commit_check",
|
|
4082
4227
|
"mem_session_end",
|
|
4083
|
-
"propose_sensor"
|
|
4228
|
+
"propose_sensor",
|
|
4229
|
+
"scaffold_test"
|
|
4084
4230
|
];
|
|
4085
4231
|
var MAINTENANCE_PROFILE_TOOLS = [
|
|
4086
4232
|
...ENFORCEMENT_PROFILE_TOOLS,
|
|
@@ -4286,6 +4432,35 @@ function createHaiveServer(options = {}) {
|
|
|
4286
4432
|
return jsonResult(await proposeSensor(input, context));
|
|
4287
4433
|
}
|
|
4288
4434
|
);
|
|
4435
|
+
registerTool(
|
|
4436
|
+
"scaffold_test",
|
|
4437
|
+
[
|
|
4438
|
+
"Generate a PENDING post-incident test from a lesson (attempt/gotcha) \u2014 the on-ramp to a command",
|
|
4439
|
+
"sensor. A command sensor routes YOUR test as its oracle, but someone has to write it; this writes",
|
|
4440
|
+
"the skeleton so you only fill in the assertion.",
|
|
4441
|
+
"",
|
|
4442
|
+
"USE THIS right after mem_tried when the mistake is behavioural (a regex can't express it): it",
|
|
4443
|
+
"writes a stub carrying the incident's provenance and returns the exact `sensors propose --kind",
|
|
4444
|
+
"test` command to arm it.",
|
|
4445
|
+
"",
|
|
4446
|
+
"It DOES NOT arm a sensor \u2014 propose_sensor stays the sole validated writer, and the stub is left",
|
|
4447
|
+
"PENDING (todo/skip) so the suite stays green until you write the assertion. Monorepo-aware: the",
|
|
4448
|
+
"framework and location come from the package that owns the lesson's anchor paths.",
|
|
4449
|
+
"",
|
|
4450
|
+
"PARAMETERS:",
|
|
4451
|
+
" memory_id \u2014 the attempt/gotcha to scaffold from",
|
|
4452
|
+
" framework \u2014 vitest | jest | pytest | gotest (auto-detected when omitted)",
|
|
4453
|
+
" out_path \u2014 override the test file path (repo-relative)",
|
|
4454
|
+
" write \u2014 write the file (default true); false returns the content for preview",
|
|
4455
|
+
"",
|
|
4456
|
+
"RETURNS: { ok, path, run_command, propose_command, content, written, already_exists, notice }"
|
|
4457
|
+
].join("\n"),
|
|
4458
|
+
ScaffoldTestInputSchema,
|
|
4459
|
+
async (input) => {
|
|
4460
|
+
tracker.record("scaffold_test", input.memory_id);
|
|
4461
|
+
return jsonResult(await scaffoldTest(input, context));
|
|
4462
|
+
}
|
|
4463
|
+
);
|
|
4289
4464
|
registerTool(
|
|
4290
4465
|
"ingest_findings",
|
|
4291
4466
|
[
|
|
@@ -4976,6 +5151,9 @@ export {
|
|
|
4976
5151
|
readPresumedCorrectTargets,
|
|
4977
5152
|
proposeSensor,
|
|
4978
5153
|
memTried,
|
|
5154
|
+
detectTestFrameworkForPaths,
|
|
5155
|
+
detectTestFrameworksForAnchors,
|
|
5156
|
+
scaffoldTest,
|
|
4979
5157
|
getBriefing,
|
|
4980
5158
|
codeMapTool,
|
|
4981
5159
|
getRecap,
|
|
@@ -5000,4 +5178,4 @@ export {
|
|
|
5000
5178
|
printHaiveMcpVersion,
|
|
5001
5179
|
runHaiveMcpStdio
|
|
5002
5180
|
};
|
|
5003
|
-
//# sourceMappingURL=chunk-
|
|
5181
|
+
//# sourceMappingURL=chunk-I4VELI5K.js.map
|