@hivelore/mcp 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/index.js +367 -198
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +62 -6
- package/dist/server.js +379 -199
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1261,6 +1261,7 @@ async function proposeSensor(input, ctx) {
|
|
|
1261
1261
|
if (!found) {
|
|
1262
1262
|
throw new Error(`No memory found with id ${input.memory_id}`);
|
|
1263
1263
|
}
|
|
1264
|
+
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}.` : "";
|
|
1264
1265
|
if (kind !== "regex") {
|
|
1265
1266
|
const verdictCmd = runCommandForValidation(input.command.trim(), ctx.paths.root, input.timeout_ms);
|
|
1266
1267
|
const anchorPathsCmd = input.paths.length > 0 ? input.paths : found.memory.frontmatter.anchor.paths;
|
|
@@ -1295,7 +1296,7 @@ ${verdictCmd.detail}`,
|
|
|
1295
1296
|
accepted: true,
|
|
1296
1297
|
memory_id: input.memory_id,
|
|
1297
1298
|
severity: input.severity,
|
|
1298
|
-
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})
|
|
1299
|
+
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,
|
|
1299
1300
|
self_check: { silent_on_current: verdictCmd.status === "passed", fires_on_bad: null, fired_on: [] }
|
|
1300
1301
|
};
|
|
1301
1302
|
}
|
|
@@ -1344,6 +1345,7 @@ ${verdictCmd.detail}`,
|
|
|
1344
1345
|
accepted: true,
|
|
1345
1346
|
memory_id: input.memory_id,
|
|
1346
1347
|
severity: input.severity,
|
|
1348
|
+
...personalScopeNudge ? { guidance: personalScopeNudge.trim() } : {},
|
|
1347
1349
|
self_check,
|
|
1348
1350
|
file_path: found.filePath
|
|
1349
1351
|
};
|
|
@@ -1354,7 +1356,9 @@ var MemTriedInputSchema = {
|
|
|
1354
1356
|
what: z16.string().min(1).describe("Brief description of the approach that was tried"),
|
|
1355
1357
|
why_failed: z16.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
1356
1358
|
instead: z16.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
1357
|
-
scope: z16.enum(["personal", "team", "module"]).
|
|
1359
|
+
scope: z16.enum(["personal", "team", "module"]).optional().describe(
|
|
1360
|
+
"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."
|
|
1361
|
+
),
|
|
1358
1362
|
module: z16.string().optional().describe("Module name (required when scope=module)"),
|
|
1359
1363
|
tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
|
|
1360
1364
|
paths: z16.array(z16.string()).default([]).describe("Anchor file paths this applies to"),
|
|
@@ -1378,10 +1382,11 @@ async function memTried(input, ctx) {
|
|
|
1378
1382
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1379
1383
|
}
|
|
1380
1384
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
1385
|
+
const scope = input.scope ?? (input.sensor ? "team" : "personal");
|
|
1381
1386
|
const baseFm = buildFrontmatter2({
|
|
1382
1387
|
type: "attempt",
|
|
1383
1388
|
slug,
|
|
1384
|
-
scope
|
|
1389
|
+
scope,
|
|
1385
1390
|
module: input.module,
|
|
1386
1391
|
tags: input.tags,
|
|
1387
1392
|
paths: input.paths,
|
|
@@ -1429,7 +1434,7 @@ async function memTried(input, ctx) {
|
|
|
1429
1434
|
...verdict.reason ? { reason: verdict.reason } : {},
|
|
1430
1435
|
...verdict.guidance ? { guidance: verdict.guidance } : {}
|
|
1431
1436
|
},
|
|
1432
|
-
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.`
|
|
1437
|
+
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." : "")
|
|
1433
1438
|
};
|
|
1434
1439
|
}
|
|
1435
1440
|
const seed = input.paths.length > 0 ? suggestSensorSeed2(body, input.paths) : null;
|
|
@@ -1450,43 +1455,177 @@ async function memTried(input, ctx) {
|
|
|
1450
1455
|
};
|
|
1451
1456
|
}
|
|
1452
1457
|
|
|
1453
|
-
// src/tools/
|
|
1454
|
-
import { existsSync as existsSync17 } from "fs";
|
|
1458
|
+
// src/tools/scaffold-test.ts
|
|
1459
|
+
import { existsSync as existsSync17, statSync } from "fs";
|
|
1455
1460
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile10 } from "fs/promises";
|
|
1456
1461
|
import path7 from "path";
|
|
1462
|
+
import { z as z17 } from "zod";
|
|
1463
|
+
import {
|
|
1464
|
+
buildProposeCommand,
|
|
1465
|
+
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
1466
|
+
normalizeFramework,
|
|
1467
|
+
parseLessonFields,
|
|
1468
|
+
pickTestFramework,
|
|
1469
|
+
scaffoldPostIncidentTest
|
|
1470
|
+
} from "@hivelore/core";
|
|
1471
|
+
var PY_SIGNALS = ["pyproject.toml", "setup.py", "pytest.ini", "requirements.txt", "tox.ini"];
|
|
1472
|
+
async function detectForAnchor(root, rel) {
|
|
1473
|
+
let dir = path7.resolve(root, rel);
|
|
1474
|
+
try {
|
|
1475
|
+
if (!statSync(dir).isDirectory()) dir = path7.dirname(dir);
|
|
1476
|
+
} catch {
|
|
1477
|
+
if (path7.extname(dir)) dir = path7.dirname(dir);
|
|
1478
|
+
}
|
|
1479
|
+
while (dir.startsWith(root)) {
|
|
1480
|
+
const pkgJson = path7.join(dir, "package.json");
|
|
1481
|
+
const hasPkg = existsSync17(pkgJson);
|
|
1482
|
+
const goMod = existsSync17(path7.join(dir, "go.mod"));
|
|
1483
|
+
const pySignal = PY_SIGNALS.some((s) => existsSync17(path7.join(dir, s)));
|
|
1484
|
+
if (hasPkg || goMod || pySignal) {
|
|
1485
|
+
let pkg = null;
|
|
1486
|
+
if (hasPkg) {
|
|
1487
|
+
try {
|
|
1488
|
+
pkg = JSON.parse(await readFile4(pkgJson, "utf8"));
|
|
1489
|
+
} catch {
|
|
1490
|
+
pkg = null;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
const baseDir = path7.relative(root, dir).split(path7.sep).join("/");
|
|
1494
|
+
return { framework: pickTestFramework(pkg, { goMod, pySignal }), baseDir };
|
|
1495
|
+
}
|
|
1496
|
+
const parent = path7.dirname(dir);
|
|
1497
|
+
if (parent === dir || dir === root) break;
|
|
1498
|
+
dir = parent;
|
|
1499
|
+
}
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
async function detectTestFrameworksForAnchors(root, anchorPaths) {
|
|
1503
|
+
const starts = anchorPaths.length > 0 ? anchorPaths : ["."];
|
|
1504
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1505
|
+
for (const rel of starts) {
|
|
1506
|
+
const found = await detectForAnchor(root, rel) ?? { framework: "vitest", baseDir: "" };
|
|
1507
|
+
const existing = groups.get(found.baseDir);
|
|
1508
|
+
if (existing) existing.anchors.push(rel);
|
|
1509
|
+
else groups.set(found.baseDir, { ...found, anchors: [rel] });
|
|
1510
|
+
}
|
|
1511
|
+
return [...groups.values()];
|
|
1512
|
+
}
|
|
1513
|
+
var ScaffoldTestInputSchema = {
|
|
1514
|
+
memory_id: z17.string().min(1).describe("Id of the attempt/gotcha lesson to scaffold a post-incident test from."),
|
|
1515
|
+
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."),
|
|
1516
|
+
out_path: z17.string().optional().describe("Override the generated test file path (repo-relative)."),
|
|
1517
|
+
write: z17.boolean().default(true).describe("Write the file to disk (default). false = return the content for preview without writing.")
|
|
1518
|
+
};
|
|
1519
|
+
async function scaffoldTest(input, ctx) {
|
|
1520
|
+
const loaded = existsSync17(ctx.paths.memoriesDir) ? await loadMemoriesFromDir14(ctx.paths.memoriesDir) : [];
|
|
1521
|
+
const found = loaded.find(({ memory }) => memory.frontmatter.id === input.memory_id);
|
|
1522
|
+
if (!found) {
|
|
1523
|
+
return { ok: false, error: `No memory found with id ${input.memory_id}`, memory_id: input.memory_id };
|
|
1524
|
+
}
|
|
1525
|
+
const anchorPaths = found.memory.frontmatter.anchor.paths ?? [];
|
|
1526
|
+
const allGroups = await detectTestFrameworksForAnchors(ctx.paths.root, anchorPaths);
|
|
1527
|
+
const groups = input.out_path ? allGroups.slice(0, 1) : allGroups;
|
|
1528
|
+
const frameworkFor = (detected) => input.framework ? normalizeFramework(input.framework) ?? detected : detected;
|
|
1529
|
+
const fields = parseLessonFields(found.memory.body);
|
|
1530
|
+
const lesson = {
|
|
1531
|
+
memoryId: input.memory_id,
|
|
1532
|
+
title: fields.title || input.memory_id,
|
|
1533
|
+
whyFailed: fields.whyFailed,
|
|
1534
|
+
instead: fields.instead,
|
|
1535
|
+
incident: found.memory.frontmatter.sensor?.incident,
|
|
1536
|
+
paths: anchorPaths
|
|
1537
|
+
};
|
|
1538
|
+
let scaffolds = groups.map(
|
|
1539
|
+
(g) => scaffoldPostIncidentTest(lesson, { framework: frameworkFor(g.framework), outPath: input.out_path, baseDir: g.baseDir })
|
|
1540
|
+
);
|
|
1541
|
+
let proposeCommand = scaffolds[0].proposeCommand;
|
|
1542
|
+
if (scaffolds.length > 1) {
|
|
1543
|
+
proposeCommand = buildProposeCommand(lesson, scaffolds.map((s) => s.runCommand).join(" && "));
|
|
1544
|
+
scaffolds = groups.map(
|
|
1545
|
+
(g) => scaffoldPostIncidentTest(lesson, {
|
|
1546
|
+
framework: frameworkFor(g.framework),
|
|
1547
|
+
baseDir: g.baseDir,
|
|
1548
|
+
proposeCommandOverride: proposeCommand
|
|
1549
|
+
})
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
const results = [];
|
|
1553
|
+
for (const scaffold of scaffolds) {
|
|
1554
|
+
const abs = path7.isAbsolute(scaffold.relPath) ? scaffold.relPath : path7.resolve(ctx.paths.root, scaffold.relPath);
|
|
1555
|
+
let written = false;
|
|
1556
|
+
let alreadyExists = false;
|
|
1557
|
+
if (input.write) {
|
|
1558
|
+
if (existsSync17(abs)) {
|
|
1559
|
+
alreadyExists = true;
|
|
1560
|
+
} else {
|
|
1561
|
+
await mkdir4(path7.dirname(abs), { recursive: true });
|
|
1562
|
+
await writeFile10(abs, scaffold.content, "utf8");
|
|
1563
|
+
written = true;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
results.push({
|
|
1567
|
+
framework: scaffold.framework,
|
|
1568
|
+
path: scaffold.relPath,
|
|
1569
|
+
run_command: scaffold.runCommand,
|
|
1570
|
+
content: scaffold.content,
|
|
1571
|
+
written,
|
|
1572
|
+
already_exists: alreadyExists
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
const first = results[0];
|
|
1576
|
+
const anyExisting = results.some((r) => r.already_exists);
|
|
1577
|
+
return {
|
|
1578
|
+
ok: true,
|
|
1579
|
+
memory_id: input.memory_id,
|
|
1580
|
+
framework: first.framework,
|
|
1581
|
+
path: first.path,
|
|
1582
|
+
run_command: first.run_command,
|
|
1583
|
+
propose_command: proposeCommand,
|
|
1584
|
+
content: first.content,
|
|
1585
|
+
written: first.written,
|
|
1586
|
+
already_exists: first.already_exists,
|
|
1587
|
+
...results.length > 1 ? { scaffolds: results } : {},
|
|
1588
|
+
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.")
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// src/tools/ingest-findings.ts
|
|
1593
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1594
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile11 } from "fs/promises";
|
|
1595
|
+
import path8 from "path";
|
|
1457
1596
|
import {
|
|
1458
1597
|
draftsFromFindings,
|
|
1459
1598
|
filterNewDrafts,
|
|
1460
|
-
loadMemoriesFromDir as
|
|
1599
|
+
loadMemoriesFromDir as loadMemoriesFromDir15,
|
|
1461
1600
|
memoryFilePath as memoryFilePath3,
|
|
1462
1601
|
parseFindings,
|
|
1463
1602
|
serializeMemory as serializeMemory9
|
|
1464
1603
|
} from "@hivelore/core";
|
|
1465
|
-
import { z as
|
|
1604
|
+
import { z as z18 } from "zod";
|
|
1466
1605
|
var IngestFindingsInputSchema = {
|
|
1467
|
-
format:
|
|
1468
|
-
report_path:
|
|
1469
|
-
report:
|
|
1470
|
-
type:
|
|
1471
|
-
scope:
|
|
1472
|
-
module:
|
|
1473
|
-
min_severity:
|
|
1474
|
-
include_stylistic:
|
|
1475
|
-
limit:
|
|
1476
|
-
author:
|
|
1477
|
-
dry_run:
|
|
1606
|
+
format: z18.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
|
|
1607
|
+
report_path: z18.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
|
|
1608
|
+
report: z18.string().optional().describe("Inline findings JSON content. Provide this OR `report_path`."),
|
|
1609
|
+
type: z18.enum(["gotcha", "convention"]).default("gotcha").describe("Memory type for the created drafts"),
|
|
1610
|
+
scope: z18.enum(["personal", "team", "module"]).default("team").describe("Visibility scope for the created memories"),
|
|
1611
|
+
module: z18.string().optional().describe("Module name (required when scope=module)"),
|
|
1612
|
+
min_severity: z18.enum(["info", "minor", "major", "critical", "blocker"]).optional().describe("Ignore findings below this severity"),
|
|
1613
|
+
include_stylistic: z18.boolean().optional().describe("Also ingest auto-fixable stylistic rules (semi/quotes/prefer-const\u2026); off by default as low-value noise"),
|
|
1614
|
+
limit: z18.number().int().positive().optional().describe("Cap the number of memories created"),
|
|
1615
|
+
author: z18.string().optional().describe("Author handle or email"),
|
|
1616
|
+
dry_run: z18.boolean().default(false).describe("When true, return the drafts that WOULD be created without writing them")
|
|
1478
1617
|
};
|
|
1479
1618
|
async function ingestFindings(input, ctx) {
|
|
1480
|
-
if (!
|
|
1619
|
+
if (!existsSync18(ctx.paths.haiveDir)) {
|
|
1481
1620
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1482
1621
|
}
|
|
1483
1622
|
let raw;
|
|
1484
1623
|
if (input.report && input.report.trim()) {
|
|
1485
1624
|
raw = input.report;
|
|
1486
1625
|
} else if (input.report_path) {
|
|
1487
|
-
const file =
|
|
1488
|
-
if (!
|
|
1489
|
-
raw = await
|
|
1626
|
+
const file = path8.resolve(ctx.paths.root, input.report_path);
|
|
1627
|
+
if (!existsSync18(file)) throw new Error(`Report file not found: ${file}`);
|
|
1628
|
+
raw = await readFile5(file, "utf8");
|
|
1490
1629
|
} else {
|
|
1491
1630
|
throw new Error("Provide either `report_path` or `report`.");
|
|
1492
1631
|
}
|
|
@@ -1500,7 +1639,7 @@ async function ingestFindings(input, ctx) {
|
|
|
1500
1639
|
...input.include_stylistic ? { includeStylistic: true } : {},
|
|
1501
1640
|
...input.limit ? { limit: input.limit } : {}
|
|
1502
1641
|
});
|
|
1503
|
-
const existing =
|
|
1642
|
+
const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir15(ctx.paths.memoriesDir) : [];
|
|
1504
1643
|
const existingTopics = new Set(
|
|
1505
1644
|
existing.map(({ memory }) => memory.frontmatter.topic).filter((t) => Boolean(t))
|
|
1506
1645
|
);
|
|
@@ -1538,22 +1677,22 @@ async function writeDraft(ctx, draft) {
|
|
|
1538
1677
|
draft.frontmatter.id,
|
|
1539
1678
|
draft.frontmatter.module
|
|
1540
1679
|
);
|
|
1541
|
-
await
|
|
1542
|
-
await
|
|
1680
|
+
await mkdir5(path8.dirname(file), { recursive: true });
|
|
1681
|
+
await writeFile11(file, serializeMemory9({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
1543
1682
|
return file;
|
|
1544
1683
|
}
|
|
1545
1684
|
|
|
1546
1685
|
// src/tools/mem-session-end.ts
|
|
1547
|
-
import { writeFile as
|
|
1548
|
-
import { existsSync as
|
|
1549
|
-
import
|
|
1686
|
+
import { writeFile as writeFile13, mkdir as mkdir7 } from "fs/promises";
|
|
1687
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1688
|
+
import path10 from "path";
|
|
1550
1689
|
import {
|
|
1551
1690
|
buildFrontmatter as buildFrontmatter3,
|
|
1552
|
-
loadMemoriesFromDir as
|
|
1691
|
+
loadMemoriesFromDir as loadMemoriesFromDir16,
|
|
1553
1692
|
memoryFilePath as memoryFilePath4,
|
|
1554
1693
|
serializeMemory as serializeMemory10
|
|
1555
1694
|
} from "@hivelore/core";
|
|
1556
|
-
import { z as
|
|
1695
|
+
import { z as z19 } from "zod";
|
|
1557
1696
|
|
|
1558
1697
|
// src/session-tracker.ts
|
|
1559
1698
|
import {
|
|
@@ -1562,12 +1701,12 @@ import {
|
|
|
1562
1701
|
loadConfig as loadConfig2,
|
|
1563
1702
|
writeSessionHandoff
|
|
1564
1703
|
} from "@hivelore/core";
|
|
1565
|
-
import { mkdir as
|
|
1566
|
-
import { existsSync as
|
|
1567
|
-
import
|
|
1704
|
+
import { mkdir as mkdir6, writeFile as writeFile12, rm } from "fs/promises";
|
|
1705
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1706
|
+
import path9 from "path";
|
|
1568
1707
|
import { execSync as execSync2 } from "child_process";
|
|
1569
1708
|
function pendingDistillPath(ctx) {
|
|
1570
|
-
return
|
|
1709
|
+
return path9.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
|
|
1571
1710
|
}
|
|
1572
1711
|
var SessionTracker = class {
|
|
1573
1712
|
events = [];
|
|
@@ -1671,7 +1810,7 @@ var SessionTracker = class {
|
|
|
1671
1810
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
1672
1811
|
);
|
|
1673
1812
|
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
1674
|
-
if (!ranPostTask && isSubstantialSession &&
|
|
1813
|
+
if (!ranPostTask && isSubstantialSession && existsSync19(this.ctx.paths.haiveDir)) {
|
|
1675
1814
|
try {
|
|
1676
1815
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
1677
1816
|
const payload = {
|
|
@@ -1684,9 +1823,9 @@ var SessionTracker = class {
|
|
|
1684
1823
|
...gitDiff ? { git_diff: gitDiff } : {},
|
|
1685
1824
|
...recapId ? { recap_id: recapId } : {}
|
|
1686
1825
|
};
|
|
1687
|
-
const cacheDir =
|
|
1688
|
-
await
|
|
1689
|
-
await
|
|
1826
|
+
const cacheDir = path9.join(this.ctx.paths.haiveDir, ".cache");
|
|
1827
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
1828
|
+
await writeFile12(
|
|
1690
1829
|
pendingDistillPath(this.ctx),
|
|
1691
1830
|
JSON.stringify(payload, null, 2) + "\n",
|
|
1692
1831
|
"utf8"
|
|
@@ -1705,7 +1844,7 @@ var SessionTracker = class {
|
|
|
1705
1844
|
};
|
|
1706
1845
|
async function clearPendingDistill(ctx) {
|
|
1707
1846
|
const p = pendingDistillPath(ctx);
|
|
1708
|
-
if (
|
|
1847
|
+
if (existsSync19(p)) {
|
|
1709
1848
|
try {
|
|
1710
1849
|
await rm(p);
|
|
1711
1850
|
} catch {
|
|
@@ -1722,15 +1861,15 @@ function summarizeTools(events) {
|
|
|
1722
1861
|
|
|
1723
1862
|
// src/tools/mem-session-end.ts
|
|
1724
1863
|
var MemSessionEndInputSchema = {
|
|
1725
|
-
goal:
|
|
1726
|
-
accomplished:
|
|
1727
|
-
discoveries:
|
|
1864
|
+
goal: z19.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1865
|
+
accomplished: z19.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1866
|
+
discoveries: z19.string().default("").describe(
|
|
1728
1867
|
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1729
1868
|
),
|
|
1730
|
-
files_touched:
|
|
1731
|
-
next_steps:
|
|
1732
|
-
scope:
|
|
1733
|
-
module:
|
|
1869
|
+
files_touched: z19.array(z19.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1870
|
+
next_steps: z19.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1871
|
+
scope: z19.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1872
|
+
module: z19.string().optional().describe("Module name (required when scope=module)")
|
|
1734
1873
|
};
|
|
1735
1874
|
function recapTopic(scope, module) {
|
|
1736
1875
|
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
@@ -1760,23 +1899,23 @@ ${input.next_steps}`);
|
|
|
1760
1899
|
return lines.join("\n");
|
|
1761
1900
|
}
|
|
1762
1901
|
async function memSessionEnd(input, ctx) {
|
|
1763
|
-
if (!
|
|
1902
|
+
if (!existsSync20(ctx.paths.haiveDir)) {
|
|
1764
1903
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'hivelore init' first.`);
|
|
1765
1904
|
}
|
|
1766
1905
|
const body = buildBody(input);
|
|
1767
1906
|
const topic = recapTopic(input.scope, input.module);
|
|
1768
1907
|
const normalizedFiles = input.files_touched.map((p) => {
|
|
1769
|
-
if (!p || !
|
|
1770
|
-
const rel =
|
|
1908
|
+
if (!p || !path10.isAbsolute(p)) return p;
|
|
1909
|
+
const rel = path10.relative(ctx.paths.root, p);
|
|
1771
1910
|
return rel.startsWith("..") ? p : rel;
|
|
1772
1911
|
});
|
|
1773
1912
|
const invalidPaths = normalizedFiles.filter(
|
|
1774
|
-
(p) => !
|
|
1913
|
+
(p) => !existsSync20(path10.resolve(ctx.paths.root, p))
|
|
1775
1914
|
);
|
|
1776
1915
|
if (invalidPaths.length > 0) {
|
|
1777
1916
|
console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
|
|
1778
1917
|
}
|
|
1779
|
-
const existing =
|
|
1918
|
+
const existing = existsSync20(ctx.paths.memoriesDir) ? await loadMemoriesFromDir16(ctx.paths.memoriesDir) : [];
|
|
1780
1919
|
const topicMatch = existing.find(
|
|
1781
1920
|
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1782
1921
|
);
|
|
@@ -1792,7 +1931,7 @@ async function memSessionEnd(input, ctx) {
|
|
|
1792
1931
|
paths: normalizedFiles.length ? normalizedFiles : fm.anchor.paths
|
|
1793
1932
|
}
|
|
1794
1933
|
};
|
|
1795
|
-
await
|
|
1934
|
+
await writeFile13(
|
|
1796
1935
|
topicMatch.filePath,
|
|
1797
1936
|
serializeMemory10({ frontmatter: newFrontmatter, body }),
|
|
1798
1937
|
"utf8"
|
|
@@ -1822,8 +1961,8 @@ async function memSessionEnd(input, ctx) {
|
|
|
1822
1961
|
frontmatter.id,
|
|
1823
1962
|
frontmatter.module
|
|
1824
1963
|
);
|
|
1825
|
-
await
|
|
1826
|
-
await
|
|
1964
|
+
await mkdir7(path10.dirname(file), { recursive: true });
|
|
1965
|
+
await writeFile13(file, serializeMemory10({ frontmatter, body }), "utf8");
|
|
1827
1966
|
await clearPendingDistill(ctx);
|
|
1828
1967
|
return {
|
|
1829
1968
|
id: frontmatter.id,
|
|
@@ -1835,9 +1974,9 @@ async function memSessionEnd(input, ctx) {
|
|
|
1835
1974
|
}
|
|
1836
1975
|
|
|
1837
1976
|
// src/tools/get-briefing.ts
|
|
1838
|
-
import { readFile as
|
|
1839
|
-
import { existsSync as
|
|
1840
|
-
import
|
|
1977
|
+
import { readFile as readFile7, writeFile as writeFile14, readdir as readdir4 } from "fs/promises";
|
|
1978
|
+
import { existsSync as existsSync22 } from "fs";
|
|
1979
|
+
import path12 from "path";
|
|
1841
1980
|
import {
|
|
1842
1981
|
allocateBudget,
|
|
1843
1982
|
assessBootstrapState,
|
|
@@ -1861,7 +2000,7 @@ import {
|
|
|
1861
2000
|
loadConfig as loadConfig3,
|
|
1862
2001
|
memoryHasExcludedTag,
|
|
1863
2002
|
hashProjectContext,
|
|
1864
|
-
loadMemoriesFromDir as
|
|
2003
|
+
loadMemoriesFromDir as loadMemoriesFromDir17,
|
|
1865
2004
|
loadPreventionEvents,
|
|
1866
2005
|
loadUsageIndex as loadUsageIndex8,
|
|
1867
2006
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
@@ -1879,12 +2018,12 @@ import {
|
|
|
1879
2018
|
truncateToTokens,
|
|
1880
2019
|
writeBriefingMarker
|
|
1881
2020
|
} from "@hivelore/core";
|
|
1882
|
-
import { z as
|
|
2021
|
+
import { z as z20 } from "zod";
|
|
1883
2022
|
|
|
1884
2023
|
// src/tools/briefing-helpers.ts
|
|
1885
|
-
import { readdir as readdir3, readFile as
|
|
1886
|
-
import { existsSync as
|
|
1887
|
-
import
|
|
2024
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
2025
|
+
import { existsSync as existsSync21 } from "fs";
|
|
2026
|
+
import path11 from "path";
|
|
1888
2027
|
import {
|
|
1889
2028
|
classifyMemoryPriority as coreClassifyPriority,
|
|
1890
2029
|
isGlobPath,
|
|
@@ -2017,16 +2156,16 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
2017
2156
|
}
|
|
2018
2157
|
async function loadModuleContexts2(ctx, modules) {
|
|
2019
2158
|
if (modules.length === 0) return [];
|
|
2020
|
-
if (!
|
|
2159
|
+
if (!existsSync21(ctx.paths.modulesContextDir)) return [];
|
|
2021
2160
|
const available = new Set(
|
|
2022
2161
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
2023
2162
|
);
|
|
2024
2163
|
const out = [];
|
|
2025
2164
|
for (const m of modules) {
|
|
2026
2165
|
if (!available.has(m)) continue;
|
|
2027
|
-
const file =
|
|
2028
|
-
if (
|
|
2029
|
-
out.push({ name: m, content: await
|
|
2166
|
+
const file = path11.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
2167
|
+
if (existsSync21(file)) {
|
|
2168
|
+
out.push({ name: m, content: await readFile6(file, "utf8") });
|
|
2030
2169
|
}
|
|
2031
2170
|
}
|
|
2032
2171
|
return out;
|
|
@@ -2034,38 +2173,38 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
2034
2173
|
|
|
2035
2174
|
// src/tools/get-briefing.ts
|
|
2036
2175
|
var GetBriefingInputSchema = {
|
|
2037
|
-
task:
|
|
2176
|
+
task: z20.string().optional().describe(
|
|
2038
2177
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
2039
2178
|
),
|
|
2040
|
-
files:
|
|
2041
|
-
max_tokens:
|
|
2179
|
+
files: z20.array(z20.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
2180
|
+
max_tokens: z20.number().int().positive().default(8e3).describe(
|
|
2042
2181
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
2043
2182
|
),
|
|
2044
|
-
max_memories:
|
|
2045
|
-
include_project_context:
|
|
2046
|
-
dedupe_project_context:
|
|
2183
|
+
max_memories: z20.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
2184
|
+
include_project_context: z20.boolean().default(true),
|
|
2185
|
+
dedupe_project_context: z20.boolean().optional().describe(
|
|
2047
2186
|
"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."
|
|
2048
2187
|
),
|
|
2049
|
-
include_module_contexts:
|
|
2050
|
-
semantic:
|
|
2188
|
+
include_module_contexts: z20.boolean().default(true),
|
|
2189
|
+
semantic: z20.boolean().default(true).describe(
|
|
2051
2190
|
"Use semantic ranking when a task is provided (requires `hivelore embeddings index`)."
|
|
2052
2191
|
),
|
|
2053
|
-
include_stale:
|
|
2054
|
-
track:
|
|
2055
|
-
format:
|
|
2192
|
+
include_stale: z20.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
2193
|
+
track: z20.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
2194
|
+
format: z20.enum(["full", "compact", "actions"]).default("full").describe(
|
|
2056
2195
|
"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."
|
|
2057
2196
|
),
|
|
2058
|
-
symbols:
|
|
2197
|
+
symbols: z20.array(z20.string()).default([]).describe(
|
|
2059
2198
|
"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."
|
|
2060
2199
|
),
|
|
2061
|
-
min_semantic_score:
|
|
2200
|
+
min_semantic_score: z20.number().min(0).max(1).default(0).describe(
|
|
2062
2201
|
"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."
|
|
2063
2202
|
),
|
|
2064
|
-
budget_preset:
|
|
2203
|
+
budget_preset: z20.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
2065
2204
|
"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."
|
|
2066
2205
|
)
|
|
2067
2206
|
};
|
|
2068
|
-
var GetBriefingZod =
|
|
2207
|
+
var GetBriefingZod = z20.object(GetBriefingInputSchema);
|
|
2069
2208
|
async function getBriefing(input, ctx) {
|
|
2070
2209
|
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
2071
2210
|
max_tokens: input.max_tokens,
|
|
@@ -2081,8 +2220,8 @@ async function getBriefing(input, ctx) {
|
|
|
2081
2220
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
2082
2221
|
let byId = /* @__PURE__ */ new Map();
|
|
2083
2222
|
let lastSession;
|
|
2084
|
-
if (
|
|
2085
|
-
const allLoaded = await
|
|
2223
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2224
|
+
const allLoaded = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2086
2225
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
2087
2226
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2088
2227
|
);
|
|
@@ -2256,7 +2395,7 @@ async function getBriefing(input, ctx) {
|
|
|
2256
2395
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
2257
2396
|
const newFm = { ...loaded.memory.frontmatter, status: "validated", validated_by: "auto" };
|
|
2258
2397
|
try {
|
|
2259
|
-
await
|
|
2398
|
+
await writeFile14(loaded.filePath, serializeMemory11({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
|
|
2260
2399
|
m.status = "validated";
|
|
2261
2400
|
m.confidence = "trusted";
|
|
2262
2401
|
} catch {
|
|
@@ -2264,7 +2403,7 @@ async function getBriefing(input, ctx) {
|
|
|
2264
2403
|
}
|
|
2265
2404
|
}
|
|
2266
2405
|
}
|
|
2267
|
-
let projectContextRaw = input.include_project_context &&
|
|
2406
|
+
let projectContextRaw = input.include_project_context && existsSync22(ctx.paths.projectContext) ? await readFile7(ctx.paths.projectContext, "utf8") : "";
|
|
2268
2407
|
let contextOmittedRecent = false;
|
|
2269
2408
|
if (projectContextRaw && input.dedupe_project_context !== false) {
|
|
2270
2409
|
const ctxHash = hashProjectContext(projectContextRaw);
|
|
@@ -2279,7 +2418,7 @@ async function getBriefing(input, ctx) {
|
|
|
2279
2418
|
const setupWarnings = [];
|
|
2280
2419
|
let autoContextGenerated = false;
|
|
2281
2420
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
2282
|
-
if ((isTemplateContext || !
|
|
2421
|
+
if ((isTemplateContext || !existsSync22(ctx.paths.projectContext)) && input.include_project_context) {
|
|
2283
2422
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
2284
2423
|
if (haiveConfig.autoContext) {
|
|
2285
2424
|
const codeMap = await loadCodeMap(ctx.paths);
|
|
@@ -2453,8 +2592,8 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2453
2592
|
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
2454
2593
|
}
|
|
2455
2594
|
}
|
|
2456
|
-
if (
|
|
2457
|
-
const allMems = await
|
|
2595
|
+
if (existsSync22(ctx.paths.memoriesDir)) {
|
|
2596
|
+
const allMems = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2458
2597
|
for (const { memory } of allMems) {
|
|
2459
2598
|
const fm = memory.frontmatter;
|
|
2460
2599
|
if (!fm.requires_human_approval) continue;
|
|
@@ -2464,9 +2603,9 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
2464
2603
|
}
|
|
2465
2604
|
}
|
|
2466
2605
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
2467
|
-
if (
|
|
2606
|
+
if (existsSync22(pendingDistillFile)) {
|
|
2468
2607
|
try {
|
|
2469
|
-
const raw = await
|
|
2608
|
+
const raw = await readFile7(pendingDistillFile, "utf8");
|
|
2470
2609
|
const pd = JSON.parse(raw);
|
|
2471
2610
|
const ageMs = Date.now() - new Date(pd.session_end).getTime();
|
|
2472
2611
|
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -2493,7 +2632,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2493
2632
|
}
|
|
2494
2633
|
}
|
|
2495
2634
|
const memoriesEmpty = outputMemories.length === 0;
|
|
2496
|
-
const hasMemoriesDir =
|
|
2635
|
+
const hasMemoriesDir = existsSync22(ctx.paths.memoriesDir);
|
|
2497
2636
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
2498
2637
|
const hasUnguessableSignal = outputMemories.some(
|
|
2499
2638
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -2508,10 +2647,10 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2508
2647
|
try {
|
|
2509
2648
|
let pcRaw = "";
|
|
2510
2649
|
try {
|
|
2511
|
-
pcRaw = await
|
|
2650
|
+
pcRaw = await readFile7(ctx.paths.projectContext, "utf8");
|
|
2512
2651
|
} catch {
|
|
2513
2652
|
}
|
|
2514
|
-
const allForBootstrap =
|
|
2653
|
+
const allForBootstrap = existsSync22(ctx.paths.memoriesDir) ? await loadMemoriesFromDir17(ctx.paths.memoriesDir) : [];
|
|
2515
2654
|
const cmForBootstrap = await loadCodeMap(ctx.paths);
|
|
2516
2655
|
let existingModules = [];
|
|
2517
2656
|
try {
|
|
@@ -2575,7 +2714,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2575
2714
|
"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."
|
|
2576
2715
|
);
|
|
2577
2716
|
}
|
|
2578
|
-
if (outputMemories.length > 0 &&
|
|
2717
|
+
if (outputMemories.length > 0 && existsSync22(ctx.paths.haiveDir)) {
|
|
2579
2718
|
const proof = briefingProofLine(await loadPreventionEvents(ctx.paths));
|
|
2580
2719
|
if (proof) hints.push(proof);
|
|
2581
2720
|
}
|
|
@@ -2589,7 +2728,7 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2589
2728
|
adaptiveTrim
|
|
2590
2729
|
});
|
|
2591
2730
|
const breadcrumbTokens = breadcrumbs ? estimateTokens([...breadcrumbs.start_here, ...breadcrumbs.drill_down, breadcrumbs.note ?? ""].join("\n")) : 0;
|
|
2592
|
-
if (
|
|
2731
|
+
if (existsSync22(ctx.paths.haiveDir)) {
|
|
2593
2732
|
await writeBriefingMarker(ctx.paths, {
|
|
2594
2733
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
2595
2734
|
...input.task ? { task: input.task } : {},
|
|
@@ -2645,10 +2784,10 @@ Invoke the \`bootstrap_repo\` MCP prompt, or close these gaps directly:
|
|
|
2645
2784
|
};
|
|
2646
2785
|
}
|
|
2647
2786
|
async function detectRunCommands(root) {
|
|
2648
|
-
const pkgPath =
|
|
2649
|
-
if (!
|
|
2787
|
+
const pkgPath = path12.join(root, "package.json");
|
|
2788
|
+
if (!existsSync22(pkgPath)) return null;
|
|
2650
2789
|
try {
|
|
2651
|
-
const pkg = JSON.parse(await
|
|
2790
|
+
const pkg = JSON.parse(await readFile7(pkgPath, "utf8"));
|
|
2652
2791
|
const scripts = pkg.scripts ?? {};
|
|
2653
2792
|
const order = ["test", "build", "lint", "typecheck", "type-check", "dev", "start"];
|
|
2654
2793
|
const lines = order.filter((name) => typeof scripts[name] === "string" && scripts[name].trim() !== "").map((name) => `- \`${name}\`: \`${scripts[name]}\``);
|
|
@@ -2713,24 +2852,24 @@ function oneLine(value) {
|
|
|
2713
2852
|
return value.replace(/\s+/g, " ").replace(/"/g, '\\"').trim().slice(0, 120);
|
|
2714
2853
|
}
|
|
2715
2854
|
function serverVersion() {
|
|
2716
|
-
return true ? "0.
|
|
2855
|
+
return true ? "0.39.0" : "dev";
|
|
2717
2856
|
}
|
|
2718
2857
|
|
|
2719
2858
|
// src/tools/code-map.ts
|
|
2720
2859
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hivelore/core";
|
|
2721
|
-
import { z as
|
|
2860
|
+
import { z as z21 } from "zod";
|
|
2722
2861
|
var CodeMapInputSchema = {
|
|
2723
|
-
file:
|
|
2724
|
-
symbol:
|
|
2725
|
-
paths:
|
|
2862
|
+
file: z21.string().optional().describe("Filter to files whose path contains this substring"),
|
|
2863
|
+
symbol: z21.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
2864
|
+
paths: z21.array(z21.string()).default([]).describe(
|
|
2726
2865
|
"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."
|
|
2727
2866
|
),
|
|
2728
|
-
max_files:
|
|
2729
|
-
max_tokens:
|
|
2867
|
+
max_files: z21.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
|
|
2868
|
+
max_tokens: z21.number().int().positive().optional().describe(
|
|
2730
2869
|
"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)."
|
|
2731
2870
|
)
|
|
2732
2871
|
};
|
|
2733
|
-
var CodeMapInputZod =
|
|
2872
|
+
var CodeMapInputZod = z21.object(CodeMapInputSchema);
|
|
2734
2873
|
async function codeMapTool(input, ctx) {
|
|
2735
2874
|
const map = await loadCodeMap2(ctx.paths);
|
|
2736
2875
|
if (!map) {
|
|
@@ -2799,18 +2938,18 @@ function estimateFileEntryTokens(f) {
|
|
|
2799
2938
|
}
|
|
2800
2939
|
|
|
2801
2940
|
// src/tools/mem-diff.ts
|
|
2802
|
-
import { existsSync as
|
|
2803
|
-
import { loadMemoriesFromDir as
|
|
2804
|
-
import { z as
|
|
2941
|
+
import { existsSync as existsSync23 } from "fs";
|
|
2942
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir18 } from "@hivelore/core";
|
|
2943
|
+
import { z as z22 } from "zod";
|
|
2805
2944
|
var MemDiffInputSchema = {
|
|
2806
|
-
id_a:
|
|
2807
|
-
id_b:
|
|
2945
|
+
id_a: z22.string().min(1).describe("First memory id"),
|
|
2946
|
+
id_b: z22.string().min(1).describe("Second memory id")
|
|
2808
2947
|
};
|
|
2809
2948
|
async function memDiff(input, ctx) {
|
|
2810
|
-
if (!
|
|
2949
|
+
if (!existsSync23(ctx.paths.memoriesDir)) {
|
|
2811
2950
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
2812
2951
|
}
|
|
2813
|
-
const all = await
|
|
2952
|
+
const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
|
|
2814
2953
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
2815
2954
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
2816
2955
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -2844,19 +2983,19 @@ async function memDiff(input, ctx) {
|
|
|
2844
2983
|
}
|
|
2845
2984
|
|
|
2846
2985
|
// src/tools/get-recap.ts
|
|
2847
|
-
import { existsSync as
|
|
2848
|
-
import { loadMemoriesFromDir as
|
|
2849
|
-
import { z as
|
|
2986
|
+
import { existsSync as existsSync24 } from "fs";
|
|
2987
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir19 } from "@hivelore/core";
|
|
2988
|
+
import { z as z23 } from "zod";
|
|
2850
2989
|
var GetRecapInputSchema = {
|
|
2851
|
-
scope:
|
|
2990
|
+
scope: z23.enum(["personal", "team", "any"]).default("any").describe(
|
|
2852
2991
|
"Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
|
|
2853
2992
|
)
|
|
2854
2993
|
};
|
|
2855
2994
|
async function getRecap(input, ctx) {
|
|
2856
|
-
if (!
|
|
2995
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
2857
2996
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
2858
2997
|
}
|
|
2859
|
-
const all = await
|
|
2998
|
+
const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
|
|
2860
2999
|
const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
|
|
2861
3000
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2862
3001
|
);
|
|
@@ -2880,13 +3019,13 @@ async function getRecap(input, ctx) {
|
|
|
2880
3019
|
}
|
|
2881
3020
|
|
|
2882
3021
|
// src/tools/mem-relevant-to.ts
|
|
2883
|
-
import { z as
|
|
3022
|
+
import { z as z24 } from "zod";
|
|
2884
3023
|
var MemRelevantToInputSchema = {
|
|
2885
|
-
task:
|
|
2886
|
-
files:
|
|
2887
|
-
limit:
|
|
2888
|
-
min_semantic_score:
|
|
2889
|
-
format:
|
|
3024
|
+
task: z24.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
|
|
3025
|
+
files: z24.array(z24.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
3026
|
+
limit: z24.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
3027
|
+
min_semantic_score: z24.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
3028
|
+
format: z24.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
2890
3029
|
};
|
|
2891
3030
|
async function memRelevantTo(input, ctx) {
|
|
2892
3031
|
const briefingInput = {
|
|
@@ -2916,14 +3055,14 @@ async function memRelevantTo(input, ctx) {
|
|
|
2916
3055
|
}
|
|
2917
3056
|
|
|
2918
3057
|
// src/tools/code-search.ts
|
|
2919
|
-
import { z as
|
|
3058
|
+
import { z as z25 } from "zod";
|
|
2920
3059
|
import { loadCodeMap as loadCodeMap3 } from "@hivelore/core";
|
|
2921
3060
|
var CodeSearchInputSchema = {
|
|
2922
|
-
query:
|
|
3061
|
+
query: z25.string().min(1).describe(
|
|
2923
3062
|
"Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
|
|
2924
3063
|
),
|
|
2925
|
-
k:
|
|
2926
|
-
min_score:
|
|
3064
|
+
k: z25.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
|
|
3065
|
+
min_score: z25.number().min(0).max(1).default(0.2).describe(
|
|
2927
3066
|
"Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
|
|
2928
3067
|
)
|
|
2929
3068
|
};
|
|
@@ -2966,7 +3105,7 @@ async function codeSearch(input, ctx) {
|
|
|
2966
3105
|
}
|
|
2967
3106
|
|
|
2968
3107
|
// src/tools/anti-patterns-check.ts
|
|
2969
|
-
import { existsSync as
|
|
3108
|
+
import { existsSync as existsSync25 } from "fs";
|
|
2970
3109
|
import {
|
|
2971
3110
|
addedLinesFromDiff,
|
|
2972
3111
|
BRIDGE_TARGET_PATH,
|
|
@@ -2976,7 +3115,7 @@ import {
|
|
|
2976
3115
|
diffHasDistinctiveOverlap,
|
|
2977
3116
|
getUsage as getUsage7,
|
|
2978
3117
|
isRetiredMemory as isRetiredMemory2,
|
|
2979
|
-
loadMemoriesFromDir as
|
|
3118
|
+
loadMemoriesFromDir as loadMemoriesFromDir20,
|
|
2980
3119
|
loadUsageIndex as loadUsageIndex9,
|
|
2981
3120
|
literalMatchesAnyToken as literalMatchesAnyToken3,
|
|
2982
3121
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3,
|
|
@@ -2985,19 +3124,19 @@ import {
|
|
|
2985
3124
|
sensorTargetsFromDiff,
|
|
2986
3125
|
tokenizeQuery as tokenizeQuery3
|
|
2987
3126
|
} from "@hivelore/core";
|
|
2988
|
-
import { z as
|
|
3127
|
+
import { z as z26 } from "zod";
|
|
2989
3128
|
var AntiPatternsCheckInputSchema = {
|
|
2990
|
-
diff:
|
|
3129
|
+
diff: z26.string().optional().describe(
|
|
2991
3130
|
"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."
|
|
2992
3131
|
),
|
|
2993
|
-
paths:
|
|
3132
|
+
paths: z26.array(z26.string()).default([]).describe(
|
|
2994
3133
|
"File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
|
|
2995
3134
|
),
|
|
2996
|
-
limit:
|
|
2997
|
-
semantic:
|
|
3135
|
+
limit: z26.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
|
|
3136
|
+
semantic: z26.boolean().default(true).describe(
|
|
2998
3137
|
"When true, also use semantic search (requires @hivelore/embeddings + memory index) to find related anti-patterns."
|
|
2999
3138
|
),
|
|
3000
|
-
min_semantic_score:
|
|
3139
|
+
min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
|
|
3001
3140
|
"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."
|
|
3002
3141
|
)
|
|
3003
3142
|
};
|
|
@@ -3081,10 +3220,10 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3081
3220
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
3082
3221
|
};
|
|
3083
3222
|
}
|
|
3084
|
-
if (!
|
|
3223
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
3085
3224
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
3086
3225
|
}
|
|
3087
|
-
const all = await
|
|
3226
|
+
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
3088
3227
|
const minSemanticScore = input.min_semantic_score ?? 0.45;
|
|
3089
3228
|
const negative = all.filter(({ memory }) => {
|
|
3090
3229
|
const t = memory.frontmatter.type;
|
|
@@ -3200,19 +3339,19 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
3200
3339
|
}
|
|
3201
3340
|
|
|
3202
3341
|
// src/tools/mem-distill.ts
|
|
3203
|
-
import { existsSync as
|
|
3342
|
+
import { existsSync as existsSync26 } from "fs";
|
|
3204
3343
|
import {
|
|
3205
|
-
loadMemoriesFromDir as
|
|
3344
|
+
loadMemoriesFromDir as loadMemoriesFromDir21,
|
|
3206
3345
|
tokenizeQuery as tokenizeQuery4
|
|
3207
3346
|
} from "@hivelore/core";
|
|
3208
|
-
import { z as
|
|
3347
|
+
import { z as z27 } from "zod";
|
|
3209
3348
|
var MemDistillInputSchema = {
|
|
3210
|
-
since_days:
|
|
3211
|
-
min_cluster:
|
|
3212
|
-
type_filter:
|
|
3349
|
+
since_days: z27.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
|
|
3350
|
+
min_cluster: z27.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
|
|
3351
|
+
type_filter: z27.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
|
|
3213
3352
|
"Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
|
|
3214
3353
|
),
|
|
3215
|
-
scope:
|
|
3354
|
+
scope: z27.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
|
|
3216
3355
|
};
|
|
3217
3356
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
3218
3357
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -3252,11 +3391,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
3252
3391
|
"error"
|
|
3253
3392
|
]);
|
|
3254
3393
|
async function memDistill(input, ctx) {
|
|
3255
|
-
if (!
|
|
3394
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
3256
3395
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
3257
3396
|
}
|
|
3258
3397
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
3259
|
-
const all = await
|
|
3398
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
3260
3399
|
const candidates = all.filter(({ memory }) => {
|
|
3261
3400
|
const fm = memory.frontmatter;
|
|
3262
3401
|
if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
|
|
@@ -3361,17 +3500,17 @@ function firstHeading(body) {
|
|
|
3361
3500
|
|
|
3362
3501
|
// src/tools/precommit-check.ts
|
|
3363
3502
|
import { pathsOverlap as pathsOverlap2 } from "@hivelore/core";
|
|
3364
|
-
import { z as
|
|
3503
|
+
import { z as z28 } from "zod";
|
|
3365
3504
|
var PreCommitCheckInputSchema = {
|
|
3366
|
-
diff:
|
|
3505
|
+
diff: z28.string().optional().describe(
|
|
3367
3506
|
"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`."
|
|
3368
3507
|
),
|
|
3369
|
-
paths:
|
|
3370
|
-
block_on:
|
|
3508
|
+
paths: z28.array(z28.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
|
|
3509
|
+
block_on: z28.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
|
|
3371
3510
|
"When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
|
|
3372
3511
|
),
|
|
3373
|
-
semantic:
|
|
3374
|
-
anchored_blocks:
|
|
3512
|
+
semantic: z28.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
|
|
3513
|
+
anchored_blocks: z28.boolean().default(false).describe(
|
|
3375
3514
|
"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."
|
|
3376
3515
|
)
|
|
3377
3516
|
};
|
|
@@ -3677,14 +3816,14 @@ function repairTargetPathForWarning(warning, paths) {
|
|
|
3677
3816
|
}
|
|
3678
3817
|
|
|
3679
3818
|
// src/tools/mem-conflict-candidates.ts
|
|
3680
|
-
import { existsSync as
|
|
3819
|
+
import { existsSync as existsSync27 } from "fs";
|
|
3681
3820
|
import {
|
|
3682
3821
|
findLexicalConflictPairs,
|
|
3683
3822
|
findTopicStatusConflictPairs,
|
|
3684
|
-
loadMemoriesFromDir as
|
|
3823
|
+
loadMemoriesFromDir as loadMemoriesFromDir22,
|
|
3685
3824
|
planConflictResolution
|
|
3686
3825
|
} from "@hivelore/core";
|
|
3687
|
-
import { z as
|
|
3826
|
+
import { z as z29 } from "zod";
|
|
3688
3827
|
function suggestResolution(byId, idA, idB) {
|
|
3689
3828
|
const a = byId.get(idA);
|
|
3690
3829
|
const b = byId.get(idB);
|
|
@@ -3698,17 +3837,17 @@ function suggestResolution(byId, idA, idB) {
|
|
|
3698
3837
|
};
|
|
3699
3838
|
}
|
|
3700
3839
|
var MemConflictCandidatesInputSchema = {
|
|
3701
|
-
since_days:
|
|
3702
|
-
types:
|
|
3703
|
-
min_jaccard:
|
|
3704
|
-
max_pairs:
|
|
3705
|
-
max_scan:
|
|
3706
|
-
max_topic_pairs:
|
|
3840
|
+
since_days: z29.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
3841
|
+
types: z29.array(z29.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
3842
|
+
min_jaccard: z29.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
3843
|
+
max_pairs: z29.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
3844
|
+
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."),
|
|
3845
|
+
max_topic_pairs: z29.number().int().positive().max(100).default(20).describe(
|
|
3707
3846
|
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
3708
3847
|
)
|
|
3709
3848
|
};
|
|
3710
3849
|
async function memConflictCandidates(input, ctx) {
|
|
3711
|
-
if (!
|
|
3850
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
3712
3851
|
return {
|
|
3713
3852
|
pairs: [],
|
|
3714
3853
|
topic_status_pairs: [],
|
|
@@ -3717,7 +3856,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3717
3856
|
notice: "No .ai/memories directory."
|
|
3718
3857
|
};
|
|
3719
3858
|
}
|
|
3720
|
-
const all = await
|
|
3859
|
+
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
3721
3860
|
const byId = new Map(all.map((m) => [m.memory.frontmatter.id, m]));
|
|
3722
3861
|
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
3723
3862
|
sinceDays: input.since_days,
|
|
@@ -3747,9 +3886,9 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3747
3886
|
|
|
3748
3887
|
// src/tools/mem-resolve-project.ts
|
|
3749
3888
|
import { resolveProjectInfo } from "@hivelore/core";
|
|
3750
|
-
import { z as
|
|
3889
|
+
import { z as z30 } from "zod";
|
|
3751
3890
|
var MemResolveProjectInputSchema = {
|
|
3752
|
-
cwd:
|
|
3891
|
+
cwd: z30.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
3753
3892
|
};
|
|
3754
3893
|
async function memResolveProject(input, _ctx) {
|
|
3755
3894
|
void _ctx;
|
|
@@ -3763,10 +3902,10 @@ async function memResolveProject(input, _ctx) {
|
|
|
3763
3902
|
|
|
3764
3903
|
// src/tools/mem-suggest-topic.ts
|
|
3765
3904
|
import { MemoryTypeSchema, suggestTopicKey } from "@hivelore/core";
|
|
3766
|
-
import { z as
|
|
3905
|
+
import { z as z31 } from "zod";
|
|
3767
3906
|
var MemSuggestTopicInputSchema = {
|
|
3768
3907
|
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
3769
|
-
title:
|
|
3908
|
+
title: z31.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
3770
3909
|
};
|
|
3771
3910
|
async function memSuggestTopic(input, _ctx) {
|
|
3772
3911
|
void _ctx;
|
|
@@ -3775,19 +3914,19 @@ async function memSuggestTopic(input, _ctx) {
|
|
|
3775
3914
|
}
|
|
3776
3915
|
|
|
3777
3916
|
// src/tools/mem-timeline.ts
|
|
3778
|
-
import { existsSync as
|
|
3779
|
-
import { collectTimelineEntries, loadMemoriesFromDir as
|
|
3780
|
-
import { z as
|
|
3917
|
+
import { existsSync as existsSync28 } from "fs";
|
|
3918
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hivelore/core";
|
|
3919
|
+
import { z as z32 } from "zod";
|
|
3781
3920
|
var MemTimelineInputSchema = {
|
|
3782
|
-
memory_id:
|
|
3783
|
-
topic:
|
|
3784
|
-
limit:
|
|
3921
|
+
memory_id: z32.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
3922
|
+
topic: z32.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
3923
|
+
limit: z32.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3785
3924
|
};
|
|
3786
3925
|
async function memTimeline(input, ctx) {
|
|
3787
|
-
if (!
|
|
3926
|
+
if (!existsSync28(ctx.paths.memoriesDir)) {
|
|
3788
3927
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3789
3928
|
}
|
|
3790
|
-
const all = await
|
|
3929
|
+
const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
|
|
3791
3930
|
const { entries, notice } = collectTimelineEntries(all, {
|
|
3792
3931
|
memoryId: input.memory_id,
|
|
3793
3932
|
topic: input.topic,
|
|
@@ -3797,12 +3936,12 @@ async function memTimeline(input, ctx) {
|
|
|
3797
3936
|
}
|
|
3798
3937
|
|
|
3799
3938
|
// src/prompts/bootstrap-project.ts
|
|
3800
|
-
import { z as
|
|
3939
|
+
import { z as z33 } from "zod";
|
|
3801
3940
|
var BootstrapProjectArgsSchema = {
|
|
3802
|
-
module:
|
|
3941
|
+
module: z33.string().optional().describe(
|
|
3803
3942
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
3804
3943
|
),
|
|
3805
|
-
focus:
|
|
3944
|
+
focus: z33.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
3806
3945
|
};
|
|
3807
3946
|
var ROOT_TEMPLATE = `# Project context
|
|
3808
3947
|
|
|
@@ -3884,25 +4023,25 @@ ${template}\`\`\`
|
|
|
3884
4023
|
}
|
|
3885
4024
|
|
|
3886
4025
|
// src/prompts/bootstrap-repo.ts
|
|
3887
|
-
import { readFile as
|
|
3888
|
-
import { existsSync as
|
|
4026
|
+
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
4027
|
+
import { existsSync as existsSync29 } from "fs";
|
|
3889
4028
|
import {
|
|
3890
4029
|
assessBootstrapState as assessBootstrapState2,
|
|
3891
4030
|
loadCodeMap as loadCodeMap4,
|
|
3892
|
-
loadMemoriesFromDir as
|
|
4031
|
+
loadMemoriesFromDir as loadMemoriesFromDir24,
|
|
3893
4032
|
renderBootstrapChecklist as renderBootstrapChecklist2
|
|
3894
4033
|
} from "@hivelore/core";
|
|
3895
|
-
import { z as
|
|
4034
|
+
import { z as z34 } from "zod";
|
|
3896
4035
|
var BootstrapRepoArgsSchema = {
|
|
3897
|
-
focus:
|
|
4036
|
+
focus: z34.string().optional().describe("Optional area to emphasize first (e.g. 'payments', 'auth').")
|
|
3898
4037
|
};
|
|
3899
4038
|
async function currentAssessment(ctx) {
|
|
3900
4039
|
let projectContextRaw = "";
|
|
3901
4040
|
try {
|
|
3902
|
-
projectContextRaw = await
|
|
4041
|
+
projectContextRaw = await readFile8(ctx.paths.projectContext, "utf8");
|
|
3903
4042
|
} catch {
|
|
3904
4043
|
}
|
|
3905
|
-
const memories =
|
|
4044
|
+
const memories = existsSync29(ctx.paths.memoriesDir) ? await loadMemoriesFromDir24(ctx.paths.memoriesDir) : [];
|
|
3906
4045
|
const codeMap = await loadCodeMap4(ctx.paths);
|
|
3907
4046
|
let existingModules = [];
|
|
3908
4047
|
try {
|
|
@@ -3970,10 +4109,10 @@ Main code areas detected: ${areas}
|
|
|
3970
4109
|
}
|
|
3971
4110
|
|
|
3972
4111
|
// src/prompts/post-task.ts
|
|
3973
|
-
import { z as
|
|
4112
|
+
import { z as z35 } from "zod";
|
|
3974
4113
|
var PostTaskArgsSchema = {
|
|
3975
|
-
task_summary:
|
|
3976
|
-
files_touched:
|
|
4114
|
+
task_summary: z35.string().optional().describe("One sentence describing what you just did"),
|
|
4115
|
+
files_touched: z35.array(z35.string()).optional().describe("Files you created or modified during the task")
|
|
3977
4116
|
};
|
|
3978
4117
|
function postTaskPrompt(args, ctx) {
|
|
3979
4118
|
const taskLine = args.task_summary ? `
|
|
@@ -4070,12 +4209,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
4070
4209
|
}
|
|
4071
4210
|
|
|
4072
4211
|
// src/prompts/import-docs.ts
|
|
4073
|
-
import { z as
|
|
4212
|
+
import { z as z36 } from "zod";
|
|
4074
4213
|
var ImportDocsArgsSchema = {
|
|
4075
|
-
content:
|
|
4076
|
-
source:
|
|
4077
|
-
scope:
|
|
4078
|
-
dry_run:
|
|
4214
|
+
content: z36.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
4215
|
+
source: z36.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
4216
|
+
scope: z36.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
4217
|
+
dry_run: z36.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
4079
4218
|
};
|
|
4080
4219
|
function importDocsPrompt(args, ctx) {
|
|
4081
4220
|
const sourceLine = args.source ? `
|
|
@@ -4141,7 +4280,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
4141
4280
|
// src/server.ts
|
|
4142
4281
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hivelore/core";
|
|
4143
4282
|
var SERVER_NAME = "hivelore";
|
|
4144
|
-
var SERVER_VERSION = "0.
|
|
4283
|
+
var SERVER_VERSION = "0.39.0";
|
|
4145
4284
|
function jsonResult(data) {
|
|
4146
4285
|
return {
|
|
4147
4286
|
content: [
|
|
@@ -4164,7 +4303,8 @@ var ENFORCEMENT_PROFILE_TOOLS = [
|
|
|
4164
4303
|
"code_search",
|
|
4165
4304
|
"pre_commit_check",
|
|
4166
4305
|
"mem_session_end",
|
|
4167
|
-
"propose_sensor"
|
|
4306
|
+
"propose_sensor",
|
|
4307
|
+
"scaffold_test"
|
|
4168
4308
|
];
|
|
4169
4309
|
var MAINTENANCE_PROFILE_TOOLS = [
|
|
4170
4310
|
...ENFORCEMENT_PROFILE_TOOLS,
|
|
@@ -4370,6 +4510,35 @@ function createHaiveServer(options = {}) {
|
|
|
4370
4510
|
return jsonResult(await proposeSensor(input, context));
|
|
4371
4511
|
}
|
|
4372
4512
|
);
|
|
4513
|
+
registerTool(
|
|
4514
|
+
"scaffold_test",
|
|
4515
|
+
[
|
|
4516
|
+
"Generate a PENDING post-incident test from a lesson (attempt/gotcha) \u2014 the on-ramp to a command",
|
|
4517
|
+
"sensor. A command sensor routes YOUR test as its oracle, but someone has to write it; this writes",
|
|
4518
|
+
"the skeleton so you only fill in the assertion.",
|
|
4519
|
+
"",
|
|
4520
|
+
"USE THIS right after mem_tried when the mistake is behavioural (a regex can't express it): it",
|
|
4521
|
+
"writes a stub carrying the incident's provenance and returns the exact `sensors propose --kind",
|
|
4522
|
+
"test` command to arm it.",
|
|
4523
|
+
"",
|
|
4524
|
+
"It DOES NOT arm a sensor \u2014 propose_sensor stays the sole validated writer, and the stub is left",
|
|
4525
|
+
"PENDING (todo/skip) so the suite stays green until you write the assertion. Monorepo-aware: the",
|
|
4526
|
+
"framework and location come from the package that owns the lesson's anchor paths.",
|
|
4527
|
+
"",
|
|
4528
|
+
"PARAMETERS:",
|
|
4529
|
+
" memory_id \u2014 the attempt/gotcha to scaffold from",
|
|
4530
|
+
" framework \u2014 vitest | jest | pytest | gotest (auto-detected when omitted)",
|
|
4531
|
+
" out_path \u2014 override the test file path (repo-relative)",
|
|
4532
|
+
" write \u2014 write the file (default true); false returns the content for preview",
|
|
4533
|
+
"",
|
|
4534
|
+
"RETURNS: { ok, path, run_command, propose_command, content, written, already_exists, notice }"
|
|
4535
|
+
].join("\n"),
|
|
4536
|
+
ScaffoldTestInputSchema,
|
|
4537
|
+
async (input) => {
|
|
4538
|
+
tracker.record("scaffold_test", input.memory_id);
|
|
4539
|
+
return jsonResult(await scaffoldTest(input, context));
|
|
4540
|
+
}
|
|
4541
|
+
);
|
|
4373
4542
|
registerTool(
|
|
4374
4543
|
"ingest_findings",
|
|
4375
4544
|
[
|