@hivelore/cli 0.36.0 → 0.38.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-EWJQ3YE7.js → chunk-UOMGIXZN.js} +313 -184
- package/dist/chunk-UOMGIXZN.js.map +1 -0
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -1
- package/dist/{server-JFLUYWUB.js → server-HG2K3WOQ.js} +8 -4
- package/package.json +4 -4
- package/dist/chunk-EWJQ3YE7.js.map +0 -1
- /package/dist/{server-JFLUYWUB.js.map → server-HG2K3WOQ.js.map} +0 -0
|
@@ -144,41 +144,52 @@ 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
|
+
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
153
|
+
normalizeFramework,
|
|
154
|
+
parseLessonFields,
|
|
155
|
+
pickTestFramework,
|
|
156
|
+
scaffoldPostIncidentTest
|
|
157
|
+
} from "@hivelore/core";
|
|
158
|
+
import { existsSync as existsSync18 } from "fs";
|
|
159
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
160
|
+
import path8 from "path";
|
|
150
161
|
import {
|
|
151
162
|
draftsFromFindings,
|
|
152
163
|
filterNewDrafts,
|
|
153
|
-
loadMemoriesFromDir as
|
|
164
|
+
loadMemoriesFromDir as loadMemoriesFromDir15,
|
|
154
165
|
memoryFilePath as memoryFilePath3,
|
|
155
166
|
parseFindings,
|
|
156
167
|
serializeMemory as serializeMemory9
|
|
157
168
|
} from "@hivelore/core";
|
|
158
|
-
import { z as
|
|
159
|
-
import { writeFile as
|
|
160
|
-
import { existsSync as
|
|
161
|
-
import
|
|
169
|
+
import { z as z18 } from "zod";
|
|
170
|
+
import { writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
171
|
+
import { existsSync as existsSync20 } from "fs";
|
|
172
|
+
import path10 from "path";
|
|
162
173
|
import {
|
|
163
174
|
buildFrontmatter as buildFrontmatter3,
|
|
164
|
-
loadMemoriesFromDir as
|
|
175
|
+
loadMemoriesFromDir as loadMemoriesFromDir16,
|
|
165
176
|
memoryFilePath as memoryFilePath4,
|
|
166
177
|
serializeMemory as serializeMemory10
|
|
167
178
|
} from "@hivelore/core";
|
|
168
|
-
import { z as
|
|
179
|
+
import { z as z19 } from "zod";
|
|
169
180
|
import {
|
|
170
181
|
appendUsageEvent,
|
|
171
182
|
appendRuntimeJournalEntry,
|
|
172
183
|
loadConfig as loadConfig2,
|
|
173
184
|
writeSessionHandoff
|
|
174
185
|
} from "@hivelore/core";
|
|
175
|
-
import { mkdir as
|
|
176
|
-
import { existsSync as
|
|
177
|
-
import
|
|
186
|
+
import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
|
|
187
|
+
import { existsSync as existsSync19 } from "fs";
|
|
188
|
+
import path9 from "path";
|
|
178
189
|
import { execSync as execSync2 } from "child_process";
|
|
179
|
-
import { readFile as
|
|
180
|
-
import { existsSync as
|
|
181
|
-
import
|
|
190
|
+
import { readFile as readFile7, writeFile as writeFile14, readdir as readdir4 } from "fs/promises";
|
|
191
|
+
import { existsSync as existsSync22 } from "fs";
|
|
192
|
+
import path12 from "path";
|
|
182
193
|
import {
|
|
183
194
|
allocateBudget,
|
|
184
195
|
assessBootstrapState,
|
|
@@ -202,7 +213,7 @@ import {
|
|
|
202
213
|
loadConfig as loadConfig3,
|
|
203
214
|
memoryHasExcludedTag,
|
|
204
215
|
hashProjectContext,
|
|
205
|
-
loadMemoriesFromDir as
|
|
216
|
+
loadMemoriesFromDir as loadMemoriesFromDir17,
|
|
206
217
|
loadPreventionEvents,
|
|
207
218
|
loadUsageIndex as loadUsageIndex8,
|
|
208
219
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
@@ -220,10 +231,10 @@ import {
|
|
|
220
231
|
truncateToTokens,
|
|
221
232
|
writeBriefingMarker
|
|
222
233
|
} from "@hivelore/core";
|
|
223
|
-
import { z as
|
|
224
|
-
import { readdir as readdir3, readFile as
|
|
225
|
-
import { existsSync as
|
|
226
|
-
import
|
|
234
|
+
import { z as z20 } from "zod";
|
|
235
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
236
|
+
import { existsSync as existsSync21 } from "fs";
|
|
237
|
+
import path11 from "path";
|
|
227
238
|
import {
|
|
228
239
|
classifyMemoryPriority as coreClassifyPriority,
|
|
229
240
|
isGlobPath,
|
|
@@ -231,17 +242,17 @@ import {
|
|
|
231
242
|
priorityRank as corePriorityRank
|
|
232
243
|
} from "@hivelore/core";
|
|
233
244
|
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
245
|
import { z as z21 } from "zod";
|
|
238
246
|
import { existsSync as existsSync23 } from "fs";
|
|
239
247
|
import { loadMemoriesFromDir as loadMemoriesFromDir18 } from "@hivelore/core";
|
|
240
248
|
import { z as z22 } from "zod";
|
|
249
|
+
import { existsSync as existsSync24 } from "fs";
|
|
250
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir19 } from "@hivelore/core";
|
|
241
251
|
import { z as z23 } from "zod";
|
|
242
252
|
import { z as z24 } from "zod";
|
|
253
|
+
import { z as z25 } from "zod";
|
|
243
254
|
import { loadCodeMap as loadCodeMap3 } from "@hivelore/core";
|
|
244
|
-
import { existsSync as
|
|
255
|
+
import { existsSync as existsSync25 } from "fs";
|
|
245
256
|
import {
|
|
246
257
|
addedLinesFromDiff,
|
|
247
258
|
BRIDGE_TARGET_PATH,
|
|
@@ -251,7 +262,7 @@ import {
|
|
|
251
262
|
diffHasDistinctiveOverlap,
|
|
252
263
|
getUsage as getUsage7,
|
|
253
264
|
isRetiredMemory as isRetiredMemory2,
|
|
254
|
-
loadMemoriesFromDir as
|
|
265
|
+
loadMemoriesFromDir as loadMemoriesFromDir20,
|
|
255
266
|
loadUsageIndex as loadUsageIndex9,
|
|
256
267
|
literalMatchesAnyToken as literalMatchesAnyToken3,
|
|
257
268
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3,
|
|
@@ -260,42 +271,42 @@ import {
|
|
|
260
271
|
sensorTargetsFromDiff,
|
|
261
272
|
tokenizeQuery as tokenizeQuery3
|
|
262
273
|
} from "@hivelore/core";
|
|
263
|
-
import { z as
|
|
264
|
-
import { existsSync as
|
|
274
|
+
import { z as z26 } from "zod";
|
|
275
|
+
import { existsSync as existsSync26 } from "fs";
|
|
265
276
|
import {
|
|
266
|
-
loadMemoriesFromDir as
|
|
277
|
+
loadMemoriesFromDir as loadMemoriesFromDir21,
|
|
267
278
|
tokenizeQuery as tokenizeQuery4
|
|
268
279
|
} from "@hivelore/core";
|
|
269
|
-
import { z as z26 } from "zod";
|
|
270
|
-
import { pathsOverlap as pathsOverlap2 } from "@hivelore/core";
|
|
271
280
|
import { z as z27 } from "zod";
|
|
272
|
-
import {
|
|
281
|
+
import { pathsOverlap as pathsOverlap2 } from "@hivelore/core";
|
|
282
|
+
import { z as z28 } from "zod";
|
|
283
|
+
import { existsSync as existsSync27 } from "fs";
|
|
273
284
|
import {
|
|
274
285
|
findLexicalConflictPairs,
|
|
275
286
|
findTopicStatusConflictPairs,
|
|
276
|
-
loadMemoriesFromDir as
|
|
287
|
+
loadMemoriesFromDir as loadMemoriesFromDir22,
|
|
277
288
|
planConflictResolution
|
|
278
289
|
} from "@hivelore/core";
|
|
279
|
-
import { z as z28 } from "zod";
|
|
280
|
-
import { resolveProjectInfo } from "@hivelore/core";
|
|
281
290
|
import { z as z29 } from "zod";
|
|
282
|
-
import {
|
|
291
|
+
import { resolveProjectInfo } from "@hivelore/core";
|
|
283
292
|
import { z as z30 } from "zod";
|
|
284
|
-
import {
|
|
285
|
-
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir22 } from "@hivelore/core";
|
|
293
|
+
import { MemoryTypeSchema, suggestTopicKey } from "@hivelore/core";
|
|
286
294
|
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
295
|
import { existsSync as existsSync28 } from "fs";
|
|
296
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hivelore/core";
|
|
297
|
+
import { z as z32 } from "zod";
|
|
298
|
+
import { z as z33 } from "zod";
|
|
299
|
+
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
300
|
+
import { existsSync as existsSync29 } from "fs";
|
|
290
301
|
import {
|
|
291
302
|
assessBootstrapState as assessBootstrapState2,
|
|
292
303
|
loadCodeMap as loadCodeMap4,
|
|
293
|
-
loadMemoriesFromDir as
|
|
304
|
+
loadMemoriesFromDir as loadMemoriesFromDir24,
|
|
294
305
|
renderBootstrapChecklist as renderBootstrapChecklist2
|
|
295
306
|
} from "@hivelore/core";
|
|
296
|
-
import { z as z33 } from "zod";
|
|
297
307
|
import { z as z34 } from "zod";
|
|
298
308
|
import { z as z35 } from "zod";
|
|
309
|
+
import { z as z36 } from "zod";
|
|
299
310
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
300
311
|
function createContext(options = {}) {
|
|
301
312
|
const env = options.env ?? process.env;
|
|
@@ -1566,30 +1577,116 @@ async function memTried(input, ctx) {
|
|
|
1566
1577
|
hint
|
|
1567
1578
|
};
|
|
1568
1579
|
}
|
|
1580
|
+
var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
|
|
1581
|
+
async function detectTestFrameworkForPaths(root, anchorPaths) {
|
|
1582
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1583
|
+
for (const rel of starts) {
|
|
1584
|
+
let dir = path7.resolve(root, rel);
|
|
1585
|
+
try {
|
|
1586
|
+
if (!statSync(dir).isDirectory()) dir = path7.dirname(dir);
|
|
1587
|
+
} catch {
|
|
1588
|
+
if (path7.extname(dir)) dir = path7.dirname(dir);
|
|
1589
|
+
}
|
|
1590
|
+
while (dir.startsWith(root)) {
|
|
1591
|
+
const pkgJson = path7.join(dir, "package.json");
|
|
1592
|
+
const hasPkg = existsSync17(pkgJson);
|
|
1593
|
+
const goMod = existsSync17(path7.join(dir, "go.mod"));
|
|
1594
|
+
const pySignal = PY_SIGNALS.some((s) => existsSync17(path7.join(dir, s)));
|
|
1595
|
+
if (hasPkg || goMod || pySignal) {
|
|
1596
|
+
let pkg = null;
|
|
1597
|
+
if (hasPkg) {
|
|
1598
|
+
try {
|
|
1599
|
+
pkg = JSON.parse(await readFile4(pkgJson, "utf8"));
|
|
1600
|
+
} catch {
|
|
1601
|
+
pkg = null;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
const baseDir = path7.relative(root, dir).split(path7.sep).join("/");
|
|
1605
|
+
return { framework: pickTestFramework(pkg, { goMod, pySignal }), baseDir };
|
|
1606
|
+
}
|
|
1607
|
+
const parent = path7.dirname(dir);
|
|
1608
|
+
if (parent === dir || dir === root) break;
|
|
1609
|
+
dir = parent;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return { framework: "vitest", baseDir: "" };
|
|
1613
|
+
}
|
|
1614
|
+
var ScaffoldTestInputSchema = {
|
|
1615
|
+
memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
|
|
1616
|
+
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."),
|
|
1617
|
+
out_path: z17.string().optional().describe("Override the generated test file path (repo-relative)."),
|
|
1618
|
+
write: z17.boolean().default(true).describe("Write the file to disk (default). false = return the content for preview without writing.")
|
|
1619
|
+
};
|
|
1620
|
+
async function scaffoldTest(input, ctx) {
|
|
1621
|
+
const loaded = existsSync17(ctx.paths.memoriesDir) ? await loadMemoriesFromDir14(ctx.paths.memoriesDir) : [];
|
|
1622
|
+
const found = loaded.find(({ memory }) => memory.frontmatter.id === input.memory_id);
|
|
1623
|
+
if (!found) {
|
|
1624
|
+
return { ok: false, error: `No memory found with id ${input.memory_id}`, memory_id: input.memory_id };
|
|
1625
|
+
}
|
|
1626
|
+
const anchorPaths = found.memory.frontmatter.anchor.paths ?? [];
|
|
1627
|
+
const detected = await detectTestFrameworkForPaths(ctx.paths.root, anchorPaths);
|
|
1628
|
+
const framework = input.framework ? normalizeFramework(input.framework) ?? detected.framework : detected.framework;
|
|
1629
|
+
const fields = parseLessonFields(found.memory.body);
|
|
1630
|
+
const scaffold = scaffoldPostIncidentTest(
|
|
1631
|
+
{
|
|
1632
|
+
memoryId: input.memory_id,
|
|
1633
|
+
title: fields.title || input.memory_id,
|
|
1634
|
+
whyFailed: fields.whyFailed,
|
|
1635
|
+
instead: fields.instead,
|
|
1636
|
+
incident: found.memory.frontmatter.sensor?.incident,
|
|
1637
|
+
paths: anchorPaths
|
|
1638
|
+
},
|
|
1639
|
+
{ framework, outPath: input.out_path, baseDir: detected.baseDir }
|
|
1640
|
+
);
|
|
1641
|
+
const abs = path7.isAbsolute(scaffold.relPath) ? scaffold.relPath : path7.resolve(ctx.paths.root, scaffold.relPath);
|
|
1642
|
+
let written = false;
|
|
1643
|
+
let alreadyExists = false;
|
|
1644
|
+
if (input.write) {
|
|
1645
|
+
if (existsSync17(abs)) {
|
|
1646
|
+
alreadyExists = true;
|
|
1647
|
+
} else {
|
|
1648
|
+
await mkdir4(path7.dirname(abs), { recursive: true });
|
|
1649
|
+
await writeFile10(abs, scaffold.content, "utf8");
|
|
1650
|
+
written = true;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return {
|
|
1654
|
+
ok: true,
|
|
1655
|
+
memory_id: input.memory_id,
|
|
1656
|
+
framework,
|
|
1657
|
+
path: scaffold.relPath,
|
|
1658
|
+
run_command: scaffold.runCommand,
|
|
1659
|
+
propose_command: scaffold.proposeCommand,
|
|
1660
|
+
content: scaffold.content,
|
|
1661
|
+
written,
|
|
1662
|
+
already_exists: alreadyExists,
|
|
1663
|
+
notice: alreadyExists ? "File already exists \u2014 not overwritten. Delete it 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."
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1569
1666
|
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:
|
|
1667
|
+
format: z18.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
|
|
1668
|
+
report_path: z18.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
|
|
1669
|
+
report: z18.string().optional().describe("Inline findings JSON content. Provide this OR `report_path`."),
|
|
1670
|
+
type: z18.enum(["gotcha", "convention"]).default("gotcha").describe("Memory type for the created drafts"),
|
|
1671
|
+
scope: z18.enum(["personal", "team", "module"]).default("team").describe("Visibility scope for the created memories"),
|
|
1672
|
+
module: z18.string().optional().describe("Module name (required when scope=module)"),
|
|
1673
|
+
min_severity: z18.enum(["info", "minor", "major", "critical", "blocker"]).optional().describe("Ignore findings below this severity"),
|
|
1674
|
+
include_stylistic: z18.boolean().optional().describe("Also ingest auto-fixable stylistic rules (semi/quotes/prefer-const\u2026); off by default as low-value noise"),
|
|
1675
|
+
limit: z18.number().int().positive().optional().describe("Cap the number of memories created"),
|
|
1676
|
+
author: z18.string().optional().describe("Author handle or email"),
|
|
1677
|
+
dry_run: z18.boolean().default(false).describe("When true, return the drafts that WOULD be created without writing them")
|
|
1581
1678
|
};
|
|
1582
1679
|
async function ingestFindings(input, ctx) {
|
|
1583
|
-
if (!
|
|
1680
|
+
if (!existsSync18(ctx.paths.haiveDir)) {
|
|
1584
1681
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1585
1682
|
}
|
|
1586
1683
|
let raw;
|
|
1587
1684
|
if (input.report && input.report.trim()) {
|
|
1588
1685
|
raw = input.report;
|
|
1589
1686
|
} else if (input.report_path) {
|
|
1590
|
-
const file =
|
|
1591
|
-
if (!
|
|
1592
|
-
raw = await
|
|
1687
|
+
const file = path8.resolve(ctx.paths.root, input.report_path);
|
|
1688
|
+
if (!existsSync18(file)) throw new Error(`Report file not found: ${file}`);
|
|
1689
|
+
raw = await readFile5(file, "utf8");
|
|
1593
1690
|
} else {
|
|
1594
1691
|
throw new Error("Provide either `report_path` or `report`.");
|
|
1595
1692
|
}
|
|
@@ -1603,7 +1700,7 @@ async function ingestFindings(input, ctx) {
|
|
|
1603
1700
|
...input.include_stylistic ? { includeStylistic: true } : {},
|
|
1604
1701
|
...input.limit ? { limit: input.limit } : {}
|
|
1605
1702
|
});
|
|
1606
|
-
const existing =
|
|
1703
|
+
const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir15(ctx.paths.memoriesDir) : [];
|
|
1607
1704
|
const existingTopics = new Set(
|
|
1608
1705
|
existing.map(({ memory }) => memory.frontmatter.topic).filter((t) => Boolean(t))
|
|
1609
1706
|
);
|
|
@@ -1641,12 +1738,12 @@ async function writeDraft(ctx, draft) {
|
|
|
1641
1738
|
draft.frontmatter.id,
|
|
1642
1739
|
draft.frontmatter.module
|
|
1643
1740
|
);
|
|
1644
|
-
await
|
|
1645
|
-
await
|
|
1741
|
+
await mkdir5(path8.dirname(file), { recursive: true });
|
|
1742
|
+
await writeFile11(file, serializeMemory9({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
1646
1743
|
return file;
|
|
1647
1744
|
}
|
|
1648
1745
|
function pendingDistillPath(ctx) {
|
|
1649
|
-
return
|
|
1746
|
+
return path9.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
|
|
1650
1747
|
}
|
|
1651
1748
|
var SessionTracker = class {
|
|
1652
1749
|
events = [];
|
|
@@ -1750,7 +1847,7 @@ var SessionTracker = class {
|
|
|
1750
1847
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
1751
1848
|
);
|
|
1752
1849
|
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
1753
|
-
if (!ranPostTask && isSubstantialSession &&
|
|
1850
|
+
if (!ranPostTask && isSubstantialSession && existsSync19(this.ctx.paths.haiveDir)) {
|
|
1754
1851
|
try {
|
|
1755
1852
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
1756
1853
|
const payload = {
|
|
@@ -1763,9 +1860,9 @@ var SessionTracker = class {
|
|
|
1763
1860
|
...gitDiff ? { git_diff: gitDiff } : {},
|
|
1764
1861
|
...recapId ? { recap_id: recapId } : {}
|
|
1765
1862
|
};
|
|
1766
|
-
const cacheDir =
|
|
1767
|
-
await
|
|
1768
|
-
await
|
|
1863
|
+
const cacheDir = path9.join(this.ctx.paths.haiveDir, ".cache");
|
|
1864
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
1865
|
+
await writeFile12(
|
|
1769
1866
|
pendingDistillPath(this.ctx),
|
|
1770
1867
|
JSON.stringify(payload, null, 2) + "\n",
|
|
1771
1868
|
"utf8"
|
|
@@ -1784,7 +1881,7 @@ var SessionTracker = class {
|
|
|
1784
1881
|
};
|
|
1785
1882
|
async function clearPendingDistill(ctx) {
|
|
1786
1883
|
const p = pendingDistillPath(ctx);
|
|
1787
|
-
if (
|
|
1884
|
+
if (existsSync19(p)) {
|
|
1788
1885
|
try {
|
|
1789
1886
|
await rm(p);
|
|
1790
1887
|
} catch {
|
|
@@ -1799,15 +1896,15 @@ function summarizeTools(events) {
|
|
|
1799
1896
|
return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} \xD7${n}`).join(", ");
|
|
1800
1897
|
}
|
|
1801
1898
|
var MemSessionEndInputSchema = {
|
|
1802
|
-
goal:
|
|
1803
|
-
accomplished:
|
|
1804
|
-
discoveries:
|
|
1899
|
+
goal: z19.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1900
|
+
accomplished: z19.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1901
|
+
discoveries: z19.string().default("").describe(
|
|
1805
1902
|
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1806
1903
|
),
|
|
1807
|
-
files_touched:
|
|
1808
|
-
next_steps:
|
|
1809
|
-
scope:
|
|
1810
|
-
module:
|
|
1904
|
+
files_touched: z19.array(z19.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1905
|
+
next_steps: z19.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1906
|
+
scope: z19.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1907
|
+
module: z19.string().optional().describe("Module name (required when scope=module)")
|
|
1811
1908
|
};
|
|
1812
1909
|
function recapTopic(scope, module) {
|
|
1813
1910
|
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
@@ -1837,23 +1934,23 @@ ${input.next_steps}`);
|
|
|
1837
1934
|
return lines.join("\n");
|
|
1838
1935
|
}
|
|
1839
1936
|
async function memSessionEnd(input, ctx) {
|
|
1840
|
-
if (!
|
|
1937
|
+
if (!existsSync20(ctx.paths.haiveDir)) {
|
|
1841
1938
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1842
1939
|
}
|
|
1843
1940
|
const body = buildBody(input);
|
|
1844
1941
|
const topic = recapTopic(input.scope, input.module);
|
|
1845
1942
|
const normalizedFiles = input.files_touched.map((p) => {
|
|
1846
|
-
if (!p || !
|
|
1847
|
-
const rel =
|
|
1943
|
+
if (!p || !path10.isAbsolute(p)) return p;
|
|
1944
|
+
const rel = path10.relative(ctx.paths.root, p);
|
|
1848
1945
|
return rel.startsWith("..") ? p : rel;
|
|
1849
1946
|
});
|
|
1850
1947
|
const invalidPaths = normalizedFiles.filter(
|
|
1851
|
-
(p) => !
|
|
1948
|
+
(p) => !existsSync20(path10.resolve(ctx.paths.root, p))
|
|
1852
1949
|
);
|
|
1853
1950
|
if (invalidPaths.length > 0) {
|
|
1854
1951
|
console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
|
|
1855
1952
|
}
|
|
1856
|
-
const existing =
|
|
1953
|
+
const existing = existsSync20(ctx.paths.memoriesDir) ? await loadMemoriesFromDir16(ctx.paths.memoriesDir) : [];
|
|
1857
1954
|
const topicMatch = existing.find(
|
|
1858
1955
|
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1859
1956
|
);
|
|
@@ -1869,7 +1966,7 @@ async function memSessionEnd(input, ctx) {
|
|
|
1869
1966
|
paths: normalizedFiles.length ? normalizedFiles : fm.anchor.paths
|
|
1870
1967
|
}
|
|
1871
1968
|
};
|
|
1872
|
-
await
|
|
1969
|
+
await writeFile13(
|
|
1873
1970
|
topicMatch.filePath,
|
|
1874
1971
|
serializeMemory10({ frontmatter: newFrontmatter, body }),
|
|
1875
1972
|
"utf8"
|
|
@@ -1899,8 +1996,8 @@ async function memSessionEnd(input, ctx) {
|
|
|
1899
1996
|
frontmatter.id,
|
|
1900
1997
|
frontmatter.module
|
|
1901
1998
|
);
|
|
1902
|
-
await
|
|
1903
|
-
await
|
|
1999
|
+
await mkdir7(path10.dirname(file), { recursive: true });
|
|
2000
|
+
await writeFile13(file, serializeMemory10({ frontmatter, body }), "utf8");
|
|
1904
2001
|
await clearPendingDistill(ctx);
|
|
1905
2002
|
return {
|
|
1906
2003
|
id: frontmatter.id,
|
|
@@ -2036,53 +2133,53 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
2036
2133
|
}
|
|
2037
2134
|
async function loadModuleContexts2(ctx, modules) {
|
|
2038
2135
|
if (modules.length === 0) return [];
|
|
2039
|
-
if (!
|
|
2136
|
+
if (!existsSync21(ctx.paths.modulesContextDir)) return [];
|
|
2040
2137
|
const available = new Set(
|
|
2041
2138
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
2042
2139
|
);
|
|
2043
2140
|
const out = [];
|
|
2044
2141
|
for (const m of modules) {
|
|
2045
2142
|
if (!available.has(m)) continue;
|
|
2046
|
-
const file =
|
|
2047
|
-
if (
|
|
2048
|
-
out.push({ name: m, content: await
|
|
2143
|
+
const file = path11.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
2144
|
+
if (existsSync21(file)) {
|
|
2145
|
+
out.push({ name: m, content: await readFile6(file, "utf8") });
|
|
2049
2146
|
}
|
|
2050
2147
|
}
|
|
2051
2148
|
return out;
|
|
2052
2149
|
}
|
|
2053
2150
|
var GetBriefingInputSchema = {
|
|
2054
|
-
task:
|
|
2151
|
+
task: z20.string().optional().describe(
|
|
2055
2152
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
2056
2153
|
),
|
|
2057
|
-
files:
|
|
2058
|
-
max_tokens:
|
|
2154
|
+
files: z20.array(z20.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
2155
|
+
max_tokens: z20.number().int().positive().default(8e3).describe(
|
|
2059
2156
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
2060
2157
|
),
|
|
2061
|
-
max_memories:
|
|
2062
|
-
include_project_context:
|
|
2063
|
-
dedupe_project_context:
|
|
2158
|
+
max_memories: z20.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
2159
|
+
include_project_context: z20.boolean().default(true),
|
|
2160
|
+
dedupe_project_context: z20.boolean().optional().describe(
|
|
2064
2161
|
"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
2162
|
),
|
|
2066
|
-
include_module_contexts:
|
|
2067
|
-
semantic:
|
|
2163
|
+
include_module_contexts: z20.boolean().default(true),
|
|
2164
|
+
semantic: z20.boolean().default(true).describe(
|
|
2068
2165
|
"Use semantic ranking when a task is provided (requires `hivelore embeddings index`)."
|
|
2069
2166
|
),
|
|
2070
|
-
include_stale:
|
|
2071
|
-
track:
|
|
2072
|
-
format:
|
|
2167
|
+
include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
2168
|
+
track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
2169
|
+
format: z20.enum(["full", "compact", "actions"]).default("full").describe(
|
|
2073
2170
|
"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
2171
|
),
|
|
2075
|
-
symbols:
|
|
2172
|
+
symbols: z20.array(z20.string()).default([]).describe(
|
|
2076
2173
|
"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
2174
|
),
|
|
2078
|
-
min_semantic_score:
|
|
2175
|
+
min_semantic_score: z20.number().min(0).max(1).default(0).describe(
|
|
2079
2176
|
"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
2177
|
),
|
|
2081
|
-
budget_preset:
|
|
2178
|
+
budget_preset: z20.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
2082
2179
|
"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
2180
|
)
|
|
2084
2181
|
};
|
|
2085
|
-
var GetBriefingZod =
|
|
2182
|
+
var GetBriefingZod = z20.object(GetBriefingInputSchema);
|
|
2086
2183
|
async function getBriefing(input, ctx) {
|
|
2087
2184
|
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
2088
2185
|
max_tokens: input.max_tokens,
|
|
@@ -2098,8 +2195,8 @@ async function getBriefing(input, ctx) {
|
|
|
2098
2195
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
2099
2196
|
let byId = /* @__PURE__ */ new Map();
|
|
2100
2197
|
let lastSession;
|
|
2101
|
-
if (
|
|
2102
|
-
const allLoaded = await
|
|
2198
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2199
|
+
const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2103
2200
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
2104
2201
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2105
2202
|
);
|
|
@@ -2273,7 +2370,7 @@ async function getBriefing(input, ctx) {
|
|
|
2273
2370
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
2274
2371
|
const newFm = { ...loaded.memory.frontmatter, status: "validated", validated_by: "auto" };
|
|
2275
2372
|
try {
|
|
2276
|
-
await
|
|
2373
|
+
await writeFile14(loaded.filePath, serializeMemory11({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
|
|
2277
2374
|
m.status = "validated";
|
|
2278
2375
|
m.confidence = "trusted";
|
|
2279
2376
|
} catch {
|
|
@@ -2281,7 +2378,7 @@ async function getBriefing(input, ctx) {
|
|
|
2281
2378
|
}
|
|
2282
2379
|
}
|
|
2283
2380
|
}
|
|
2284
|
-
let projectContextRaw = input.include_project_context &&
|
|
2381
|
+
let projectContextRaw = input.include_project_context && existsSync22(ctx.paths.projectContext) ? await readFile7(ctx.paths.projectContext, "utf8") : "";
|
|
2285
2382
|
let contextOmittedRecent = false;
|
|
2286
2383
|
if (projectContextRaw && input.dedupe_project_context !== false) {
|
|
2287
2384
|
const ctxHash = hashProjectContext(projectContextRaw);
|
|
@@ -2296,7 +2393,7 @@ async function getBriefing(input, ctx) {
|
|
|
2296
2393
|
const setupWarnings = [];
|
|
2297
2394
|
let autoContextGenerated = false;
|
|
2298
2395
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
2299
|
-
if ((isTemplateContext || !
|
|
2396
|
+
if ((isTemplateContext || !existsSync22(ctx.paths.projectContext)) && input.include_project_context) {
|
|
2300
2397
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
2301
2398
|
if (haiveConfig.autoContext) {
|
|
2302
2399
|
const codeMap = await loadCodeMap(ctx.paths);
|
|
@@ -2470,8 +2567,8 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2470
2567
|
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
2471
2568
|
}
|
|
2472
2569
|
}
|
|
2473
|
-
if (
|
|
2474
|
-
const allMems = await
|
|
2570
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2571
|
+
const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2475
2572
|
for (const { memory } of allMems) {
|
|
2476
2573
|
const fm = memory.frontmatter;
|
|
2477
2574
|
if (!fm.requires_human_approval) continue;
|
|
@@ -2481,9 +2578,9 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2481
2578
|
}
|
|
2482
2579
|
}
|
|
2483
2580
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
2484
|
-
if (
|
|
2581
|
+
if (existsSync22(pendingDistillFile)) {
|
|
2485
2582
|
try {
|
|
2486
|
-
const raw = await
|
|
2583
|
+
const raw = await readFile7(pendingDistillFile, "utf8");
|
|
2487
2584
|
const pd = JSON.parse(raw);
|
|
2488
2585
|
const ageMs = Date.now() - new Date(pd.session_end).getTime();
|
|
2489
2586
|
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -2510,7 +2607,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2510
2607
|
}
|
|
2511
2608
|
}
|
|
2512
2609
|
const memoriesEmpty = outputMemories.length === 0;
|
|
2513
|
-
const hasMemoriesDir =
|
|
2610
|
+
const hasMemoriesDir = existsSync22(ctx.paths.memoriesDir);
|
|
2514
2611
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
2515
2612
|
const hasUnguessableSignal = outputMemories.some(
|
|
2516
2613
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -2525,10 +2622,10 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2525
2622
|
try {
|
|
2526
2623
|
let pcRaw = "";
|
|
2527
2624
|
try {
|
|
2528
|
-
pcRaw = await
|
|
2625
|
+
pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
|
|
2529
2626
|
} catch {
|
|
2530
2627
|
}
|
|
2531
|
-
const allForBootstrap =
|
|
2628
|
+
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
|
|
2532
2629
|
const cmForBootstrap = await loadCodeMap(ctx.paths);
|
|
2533
2630
|
let existingModules = [];
|
|
2534
2631
|
try {
|
|
@@ -2592,7 +2689,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2592
2689
|
"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
2690
|
);
|
|
2594
2691
|
}
|
|
2595
|
-
if (outputMemories.length > 0 &&
|
|
2692
|
+
if (outputMemories.length > 0 && existsSync22(ctx.paths.haiveDir)) {
|
|
2596
2693
|
const proof = briefingProofLine(await loadPreventionEvents(ctx.paths));
|
|
2597
2694
|
if (proof) hints.push(proof);
|
|
2598
2695
|
}
|
|
@@ -2606,7 +2703,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2606
2703
|
adaptiveTrim
|
|
2607
2704
|
});
|
|
2608
2705
|
const breadcrumbTokens = breadcrumbs ? estimateTokens([...breadcrumbs.start_here, ...breadcrumbs.drill_down, breadcrumbs.note ?? ""].join("\n")) : 0;
|
|
2609
|
-
if (
|
|
2706
|
+
if (existsSync22(ctx.paths.haiveDir)) {
|
|
2610
2707
|
await writeBriefingMarker(ctx.paths, {
|
|
2611
2708
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
2612
2709
|
...input.task ? { task: input.task } : {},
|
|
@@ -2662,10 +2759,10 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2662
2759
|
};
|
|
2663
2760
|
}
|
|
2664
2761
|
async function detectRunCommands(root) {
|
|
2665
|
-
const pkgPath =
|
|
2666
|
-
if (!
|
|
2762
|
+
const pkgPath = path12.join(root, "package.json");
|
|
2763
|
+
if (!existsSync22(pkgPath)) return null;
|
|
2667
2764
|
try {
|
|
2668
|
-
const pkg = JSON.parse(await
|
|
2765
|
+
const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
|
|
2669
2766
|
const scripts = pkg.scripts ?? {};
|
|
2670
2767
|
const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
|
|
2671
2768
|
const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
|
|
@@ -2730,20 +2827,20 @@ function oneLine(value) {
|
|
|
2730
2827
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
2731
2828
|
}
|
|
2732
2829
|
function serverVersion() {
|
|
2733
|
-
return true ? "0.
|
|
2830
|
+
return true ? "0.38.0" : "dev";
|
|
2734
2831
|
}
|
|
2735
2832
|
var CodeMapInputSchema = {
|
|
2736
|
-
file:
|
|
2737
|
-
symbol:
|
|
2738
|
-
paths:
|
|
2833
|
+
file: z21.string().optional().describe("Filter to files whose path contains this substring"),
|
|
2834
|
+
symbol: z21.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
2835
|
+
paths: z21.array(z21.string()).default([]).describe(
|
|
2739
2836
|
"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
2837
|
),
|
|
2741
|
-
max_files:
|
|
2742
|
-
max_tokens:
|
|
2838
|
+
max_files: z21.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
|
|
2839
|
+
max_tokens: z21.number().int().positive().optional().describe(
|
|
2743
2840
|
"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
2841
|
)
|
|
2745
2842
|
};
|
|
2746
|
-
var CodeMapInputZod =
|
|
2843
|
+
var CodeMapInputZod = z21.object(CodeMapInputSchema);
|
|
2747
2844
|
async function codeMapTool(input, ctx) {
|
|
2748
2845
|
const map = await loadCodeMap2(ctx.paths);
|
|
2749
2846
|
if (!map) {
|
|
@@ -2811,14 +2908,14 @@ function estimateFileEntryTokens(f) {
|
|
|
2811
2908
|
return estimateTokens2(f.path) + estimateTokens2(f.entry.summary ?? "") + exportsCost + 4;
|
|
2812
2909
|
}
|
|
2813
2910
|
var MemDiffInputSchema = {
|
|
2814
|
-
id_a:
|
|
2815
|
-
id_b:
|
|
2911
|
+
id_a: z22.string().min(1).describe("First memory id"),
|
|
2912
|
+
id_b: z22.string().min(1).describe("Second memory id")
|
|
2816
2913
|
};
|
|
2817
2914
|
async function memDiff(input, ctx) {
|
|
2818
|
-
if (!
|
|
2915
|
+
if (!existsSync23(ctx.paths.memoriesDir)) {
|
|
2819
2916
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
2820
2917
|
}
|
|
2821
|
-
const all = await
|
|
2918
|
+
const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
|
|
2822
2919
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
2823
2920
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
2824
2921
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -2851,15 +2948,15 @@ async function memDiff(input, ctx) {
|
|
|
2851
2948
|
};
|
|
2852
2949
|
}
|
|
2853
2950
|
var GetRecapInputSchema = {
|
|
2854
|
-
scope:
|
|
2951
|
+
scope: z23.enum(["personal", "team", "any"]).default("any").describe(
|
|
2855
2952
|
"Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
|
|
2856
2953
|
)
|
|
2857
2954
|
};
|
|
2858
2955
|
async function getRecap(input, ctx) {
|
|
2859
|
-
if (!
|
|
2956
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
2860
2957
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
2861
2958
|
}
|
|
2862
|
-
const all = await
|
|
2959
|
+
const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
|
|
2863
2960
|
const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
|
|
2864
2961
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2865
2962
|
);
|
|
@@ -2882,11 +2979,11 @@ async function getRecap(input, ctx) {
|
|
|
2882
2979
|
};
|
|
2883
2980
|
}
|
|
2884
2981
|
var MemRelevantToInputSchema = {
|
|
2885
|
-
task:
|
|
2886
|
-
files:
|
|
2887
|
-
limit:
|
|
2888
|
-
min_semantic_score:
|
|
2889
|
-
format:
|
|
2982
|
+
task: z24.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
|
|
2983
|
+
files: z24.array(z24.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
2984
|
+
limit: z24.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
2985
|
+
min_semantic_score: z24.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
2986
|
+
format: z24.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
2890
2987
|
};
|
|
2891
2988
|
async function memRelevantTo(input, ctx) {
|
|
2892
2989
|
const briefingInput = {
|
|
@@ -2915,11 +3012,11 @@ async function memRelevantTo(input, ctx) {
|
|
|
2915
3012
|
return out;
|
|
2916
3013
|
}
|
|
2917
3014
|
var CodeSearchInputSchema = {
|
|
2918
|
-
query:
|
|
3015
|
+
query: z25.string().min(1).describe(
|
|
2919
3016
|
"Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
|
|
2920
3017
|
),
|
|
2921
|
-
k:
|
|
2922
|
-
min_score:
|
|
3018
|
+
k: z25.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
|
|
3019
|
+
min_score: z25.number().min(0).max(1).default(0.2).describe(
|
|
2923
3020
|
"Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
|
|
2924
3021
|
)
|
|
2925
3022
|
};
|
|
@@ -2961,17 +3058,17 @@ async function codeSearch(input, ctx) {
|
|
|
2961
3058
|
};
|
|
2962
3059
|
}
|
|
2963
3060
|
var AntiPatternsCheckInputSchema = {
|
|
2964
|
-
diff:
|
|
3061
|
+
diff: z26.string().optional().describe(
|
|
2965
3062
|
"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
3063
|
),
|
|
2967
|
-
paths:
|
|
3064
|
+
paths: z26.array(z26.string()).default([]).describe(
|
|
2968
3065
|
"File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
|
|
2969
3066
|
),
|
|
2970
|
-
limit:
|
|
2971
|
-
semantic:
|
|
3067
|
+
limit: z26.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
|
|
3068
|
+
semantic: z26.boolean().default(true).describe(
|
|
2972
3069
|
"When true, also use semantic search (requires @hivelore/embeddings + memory index) to find related anti-patterns."
|
|
2973
3070
|
),
|
|
2974
|
-
min_semantic_score:
|
|
3071
|
+
min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
|
|
2975
3072
|
"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
3073
|
)
|
|
2977
3074
|
};
|
|
@@ -3055,10 +3152,10 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3055
3152
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
3056
3153
|
};
|
|
3057
3154
|
}
|
|
3058
|
-
if (!
|
|
3155
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
3059
3156
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
3060
3157
|
}
|
|
3061
|
-
const all = await
|
|
3158
|
+
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
3062
3159
|
const minSemanticScore = input.min_semantic_score ?? 0.45;
|
|
3063
3160
|
const negative = all.filter(({ memory }) => {
|
|
3064
3161
|
const t = memory.frontmatter.type;
|
|
@@ -3173,12 +3270,12 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3173
3270
|
};
|
|
3174
3271
|
}
|
|
3175
3272
|
var MemDistillInputSchema = {
|
|
3176
|
-
since_days:
|
|
3177
|
-
min_cluster:
|
|
3178
|
-
type_filter:
|
|
3273
|
+
since_days: z27.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
|
|
3274
|
+
min_cluster: z27.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
|
|
3275
|
+
type_filter: z27.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
|
|
3179
3276
|
"Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
|
|
3180
3277
|
),
|
|
3181
|
-
scope:
|
|
3278
|
+
scope: z27.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
|
|
3182
3279
|
};
|
|
3183
3280
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
3184
3281
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -3218,11 +3315,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
3218
3315
|
"error"
|
|
3219
3316
|
]);
|
|
3220
3317
|
async function memDistill(input, ctx) {
|
|
3221
|
-
if (!
|
|
3318
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
3222
3319
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
3223
3320
|
}
|
|
3224
3321
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
3225
|
-
const all = await
|
|
3322
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
3226
3323
|
const candidates = all.filter(({ memory }) => {
|
|
3227
3324
|
const fm = memory.frontmatter;
|
|
3228
3325
|
if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
|
|
@@ -3325,15 +3422,15 @@ function firstHeading(body) {
|
|
|
3325
3422
|
return void 0;
|
|
3326
3423
|
}
|
|
3327
3424
|
var PreCommitCheckInputSchema = {
|
|
3328
|
-
diff:
|
|
3425
|
+
diff: z28.string().optional().describe(
|
|
3329
3426
|
"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
3427
|
),
|
|
3331
|
-
paths:
|
|
3332
|
-
block_on:
|
|
3428
|
+
paths: z28.array(z28.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
|
|
3429
|
+
block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
|
|
3333
3430
|
"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
3431
|
),
|
|
3335
|
-
semantic:
|
|
3336
|
-
anchored_blocks:
|
|
3432
|
+
semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
|
|
3433
|
+
anchored_blocks: z28.boolean().default(false).describe(
|
|
3337
3434
|
"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
3435
|
)
|
|
3339
3436
|
};
|
|
@@ -3650,17 +3747,17 @@ function suggestResolution(byId, idA, idB) {
|
|
|
3650
3747
|
};
|
|
3651
3748
|
}
|
|
3652
3749
|
var MemConflictCandidatesInputSchema = {
|
|
3653
|
-
since_days:
|
|
3654
|
-
types:
|
|
3655
|
-
min_jaccard:
|
|
3656
|
-
max_pairs:
|
|
3657
|
-
max_scan:
|
|
3658
|
-
max_topic_pairs:
|
|
3750
|
+
since_days: z29.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
3751
|
+
types: z29.array(z29.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
3752
|
+
min_jaccard: z29.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
3753
|
+
max_pairs: z29.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
3754
|
+
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."),
|
|
3755
|
+
max_topic_pairs: z29.number().int().positive().max(100).default(20).describe(
|
|
3659
3756
|
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
3660
3757
|
)
|
|
3661
3758
|
};
|
|
3662
3759
|
async function memConflictCandidates(input, ctx) {
|
|
3663
|
-
if (!
|
|
3760
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
3664
3761
|
return {
|
|
3665
3762
|
pairs: [],
|
|
3666
3763
|
topic_status_pairs: [],
|
|
@@ -3669,7 +3766,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3669
3766
|
notice: "No .ai/memories directory."
|
|
3670
3767
|
};
|
|
3671
3768
|
}
|
|
3672
|
-
const all = await
|
|
3769
|
+
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
3673
3770
|
const byId = new Map(all.map((m) => [m.memory.frontmatter.id, m]));
|
|
3674
3771
|
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
3675
3772
|
sinceDays: input.since_days,
|
|
@@ -3697,7 +3794,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3697
3794
|
};
|
|
3698
3795
|
}
|
|
3699
3796
|
var MemResolveProjectInputSchema = {
|
|
3700
|
-
cwd:
|
|
3797
|
+
cwd: z30.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
3701
3798
|
};
|
|
3702
3799
|
async function memResolveProject(input, _ctx) {
|
|
3703
3800
|
void _ctx;
|
|
@@ -3710,7 +3807,7 @@ async function memResolveProject(input, _ctx) {
|
|
|
3710
3807
|
}
|
|
3711
3808
|
var MemSuggestTopicInputSchema = {
|
|
3712
3809
|
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
3713
|
-
title:
|
|
3810
|
+
title: z31.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
3714
3811
|
};
|
|
3715
3812
|
async function memSuggestTopic(input, _ctx) {
|
|
3716
3813
|
void _ctx;
|
|
@@ -3718,15 +3815,15 @@ async function memSuggestTopic(input, _ctx) {
|
|
|
3718
3815
|
return { topic_key: suggestion.topic_key, family: suggestion.family, type: input.type };
|
|
3719
3816
|
}
|
|
3720
3817
|
var MemTimelineInputSchema = {
|
|
3721
|
-
memory_id:
|
|
3722
|
-
topic:
|
|
3723
|
-
limit:
|
|
3818
|
+
memory_id: z32.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
3819
|
+
topic: z32.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
3820
|
+
limit: z32.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3724
3821
|
};
|
|
3725
3822
|
async function memTimeline(input, ctx) {
|
|
3726
|
-
if (!
|
|
3823
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
3727
3824
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3728
3825
|
}
|
|
3729
|
-
const all = await
|
|
3826
|
+
const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
|
|
3730
3827
|
const { entries, notice } = collectTimelineEntries(all, {
|
|
3731
3828
|
memoryId: input.memory_id,
|
|
3732
3829
|
topic: input.topic,
|
|
@@ -3735,10 +3832,10 @@ async function memTimeline(input, ctx) {
|
|
|
3735
3832
|
return { entries, total: entries.length, notice };
|
|
3736
3833
|
}
|
|
3737
3834
|
var BootstrapProjectArgsSchema = {
|
|
3738
|
-
module:
|
|
3835
|
+
module: z33.string().optional().describe(
|
|
3739
3836
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
3740
3837
|
),
|
|
3741
|
-
focus:
|
|
3838
|
+
focus: z33.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
3742
3839
|
};
|
|
3743
3840
|
var ROOT_TEMPLATE = `# Project context
|
|
3744
3841
|
|
|
@@ -3819,15 +3916,15 @@ ${template}\`\`\`
|
|
|
3819
3916
|
};
|
|
3820
3917
|
}
|
|
3821
3918
|
var BootstrapRepoArgsSchema = {
|
|
3822
|
-
focus:
|
|
3919
|
+
focus: z34.string().optional().describe("Optional area to emphasize first (e.g. 'payments', 'auth').")
|
|
3823
3920
|
};
|
|
3824
3921
|
async function currentAssessment(ctx) {
|
|
3825
3922
|
let projectContextRaw = "";
|
|
3826
3923
|
try {
|
|
3827
|
-
projectContextRaw = await
|
|
3924
|
+
projectContextRaw = await readFile8(ctx.paths.projectContext, "utf8");
|
|
3828
3925
|
} catch {
|
|
3829
3926
|
}
|
|
3830
|
-
const memories =
|
|
3927
|
+
const memories = existsSync29(ctx.paths.memoriesDir) ? await loadMemoriesFromDir24(ctx.paths.memoriesDir) : [];
|
|
3831
3928
|
const codeMap = await loadCodeMap4(ctx.paths);
|
|
3832
3929
|
let existingModules = [];
|
|
3833
3930
|
try {
|
|
@@ -3894,8 +3991,8 @@ Main code areas detected: ${areas}
|
|
|
3894
3991
|
};
|
|
3895
3992
|
}
|
|
3896
3993
|
var PostTaskArgsSchema = {
|
|
3897
|
-
task_summary:
|
|
3898
|
-
files_touched:
|
|
3994
|
+
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
3995
|
+
files_touched: z35.array(z35.string()).optional().describe("Files you created or modified during the task")
|
|
3899
3996
|
};
|
|
3900
3997
|
function postTaskPrompt(args, ctx) {
|
|
3901
3998
|
const taskLine = args.task_summary ? `
|
|
@@ -3991,10 +4088,10 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
3991
4088
|
};
|
|
3992
4089
|
}
|
|
3993
4090
|
var ImportDocsArgsSchema = {
|
|
3994
|
-
content:
|
|
3995
|
-
source:
|
|
3996
|
-
scope:
|
|
3997
|
-
dry_run:
|
|
4091
|
+
content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
4092
|
+
source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
4093
|
+
scope: z36.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
4094
|
+
dry_run: z36.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
3998
4095
|
};
|
|
3999
4096
|
function importDocsPrompt(args, ctx) {
|
|
4000
4097
|
const sourceLine = args.source ? `
|
|
@@ -4057,7 +4154,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4057
4154
|
};
|
|
4058
4155
|
}
|
|
4059
4156
|
var SERVER_NAME = "hivelore";
|
|
4060
|
-
var SERVER_VERSION = "0.
|
|
4157
|
+
var SERVER_VERSION = "0.38.0";
|
|
4061
4158
|
function jsonResult(data) {
|
|
4062
4159
|
return {
|
|
4063
4160
|
content: [
|
|
@@ -4080,7 +4177,8 @@ var ENFORCEMENT_PROFILE_TOOLS = [
|
|
|
4080
4177
|
"code_search",
|
|
4081
4178
|
"pre_commit_check",
|
|
4082
4179
|
"mem_session_end",
|
|
4083
|
-
"propose_sensor"
|
|
4180
|
+
"propose_sensor",
|
|
4181
|
+
"scaffold_test"
|
|
4084
4182
|
];
|
|
4085
4183
|
var MAINTENANCE_PROFILE_TOOLS = [
|
|
4086
4184
|
...ENFORCEMENT_PROFILE_TOOLS,
|
|
@@ -4286,6 +4384,35 @@ function createHaiveServer(options = {}) {
|
|
|
4286
4384
|
return jsonResult(await proposeSensor(input, context));
|
|
4287
4385
|
}
|
|
4288
4386
|
);
|
|
4387
|
+
registerTool(
|
|
4388
|
+
"scaffold_test",
|
|
4389
|
+
[
|
|
4390
|
+
"Generate a PENDING post-incident test from a lesson (attempt/gotcha) \u2014 the on-ramp to a command",
|
|
4391
|
+
"sensor. A command sensor routes YOUR test as its oracle, but someone has to write it; this writes",
|
|
4392
|
+
"the skeleton so you only fill in the assertion.",
|
|
4393
|
+
"",
|
|
4394
|
+
"USE THIS right after mem_tried when the mistake is behavioural (a regex can't express it): it",
|
|
4395
|
+
"writes a stub carrying the incident's provenance and returns the exact `sensors propose --kind",
|
|
4396
|
+
"test` command to arm it.",
|
|
4397
|
+
"",
|
|
4398
|
+
"It DOES NOT arm a sensor \u2014 propose_sensor stays the sole validated writer, and the stub is left",
|
|
4399
|
+
"PENDING (todo/skip) so the suite stays green until you write the assertion. Monorepo-aware: the",
|
|
4400
|
+
"framework and location come from the package that owns the lesson's anchor paths.",
|
|
4401
|
+
"",
|
|
4402
|
+
"PARAMETERS:",
|
|
4403
|
+
" memory_id \u2014 the attempt/gotcha to scaffold from",
|
|
4404
|
+
" framework \u2014 vitest | jest | pytest | gotest (auto-detected when omitted)",
|
|
4405
|
+
" out_path \u2014 override the test file path (repo-relative)",
|
|
4406
|
+
" write \u2014 write the file (default true); false returns the content for preview",
|
|
4407
|
+
"",
|
|
4408
|
+
"RETURNS: { ok, path, run_command, propose_command, content, written, already_exists, notice }"
|
|
4409
|
+
].join("\n"),
|
|
4410
|
+
ScaffoldTestInputSchema,
|
|
4411
|
+
async (input) => {
|
|
4412
|
+
tracker.record("scaffold_test", input.memory_id);
|
|
4413
|
+
return jsonResult(await scaffoldTest(input, context));
|
|
4414
|
+
}
|
|
4415
|
+
);
|
|
4289
4416
|
registerTool(
|
|
4290
4417
|
"ingest_findings",
|
|
4291
4418
|
[
|
|
@@ -4976,6 +5103,8 @@ export {
|
|
|
4976
5103
|
readPresumedCorrectTargets,
|
|
4977
5104
|
proposeSensor,
|
|
4978
5105
|
memTried,
|
|
5106
|
+
detectTestFrameworkForPaths,
|
|
5107
|
+
scaffoldTest,
|
|
4979
5108
|
getBriefing,
|
|
4980
5109
|
codeMapTool,
|
|
4981
5110
|
getRecap,
|
|
@@ -5000,4 +5129,4 @@ export {
|
|
|
5000
5129
|
printHaiveMcpVersion,
|
|
5001
5130
|
runHaiveMcpStdio
|
|
5002
5131
|
};
|
|
5003
|
-
//# sourceMappingURL=chunk-
|
|
5132
|
+
//# sourceMappingURL=chunk-UOMGIXZN.js.map
|