@hiveai/mcp 0.12.3 → 0.12.9

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/server.js CHANGED
@@ -1141,32 +1141,123 @@ async function memTried(input, ctx) {
1141
1141
  return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
1142
1142
  }
1143
1143
 
1144
- // src/tools/mem-observe.ts
1145
- import { mkdir as mkdir4, writeFile as writeFile8 } from "fs/promises";
1144
+ // src/tools/ingest-findings.ts
1146
1145
  import { existsSync as existsSync16 } from "fs";
1146
+ import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile8 } from "fs/promises";
1147
1147
  import path6 from "path";
1148
1148
  import {
1149
- buildFrontmatter as buildFrontmatter3,
1150
- isLikelyGuessable,
1149
+ draftsFromFindings,
1150
+ filterNewDrafts,
1151
+ loadMemoriesFromDir as loadMemoriesFromDir13,
1151
1152
  memoryFilePath as memoryFilePath3,
1153
+ parseFindings,
1152
1154
  serializeMemory as serializeMemory7
1153
1155
  } from "@hiveai/core";
1154
1156
  import { z as z16 } from "zod";
1155
- var MemObserveInputSchema = {
1156
- what: z16.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
1157
- where: z16.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
1158
- impact: z16.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
1159
- fix: z16.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
1160
- scope: z16.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
1157
+ var IngestFindingsInputSchema = {
1158
+ format: z16.enum(["sarif", "sonar"]).describe("Report format: 'sarif' (ESLint/Semgrep/CodeQL) or 'sonar' (SonarQube issues JSON)"),
1159
+ report_path: z16.string().optional().describe("Project-relative path to the findings JSON file. Provide this OR `report`."),
1160
+ report: z16.string().optional().describe("Inline findings JSON content. Provide this OR `report_path`."),
1161
+ type: z16.enum(["gotcha", "convention"]).default("gotcha").describe("Memory type for the created drafts"),
1162
+ scope: z16.enum(["personal", "team", "module"]).default("team").describe("Visibility scope for the created memories"),
1161
1163
  module: z16.string().optional().describe("Module name (required when scope=module)"),
1162
- tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
1164
+ min_severity: z16.enum(["info", "minor", "major", "critical", "blocker"]).optional().describe("Ignore findings below this severity"),
1165
+ limit: z16.number().int().positive().optional().describe("Cap the number of memories created"),
1163
1166
  author: z16.string().optional().describe("Author handle or email"),
1164
- force: z16.boolean().default(false).describe(
1167
+ dry_run: z16.boolean().default(false).describe("When true, return the drafts that WOULD be created without writing them")
1168
+ };
1169
+ async function ingestFindings(input, ctx) {
1170
+ if (!existsSync16(ctx.paths.haiveDir)) {
1171
+ throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1172
+ }
1173
+ let raw;
1174
+ if (input.report && input.report.trim()) {
1175
+ raw = input.report;
1176
+ } else if (input.report_path) {
1177
+ const file = path6.resolve(ctx.paths.root, input.report_path);
1178
+ if (!existsSync16(file)) throw new Error(`Report file not found: ${file}`);
1179
+ raw = await readFile3(file, "utf8");
1180
+ } else {
1181
+ throw new Error("Provide either `report_path` or `report`.");
1182
+ }
1183
+ const findings = parseFindings(input.format, raw);
1184
+ const drafts = draftsFromFindings(findings, {
1185
+ type: input.type,
1186
+ scope: input.scope,
1187
+ module: input.module,
1188
+ author: input.author,
1189
+ ...input.min_severity ? { minSeverity: input.min_severity } : {},
1190
+ ...input.limit ? { limit: input.limit } : {}
1191
+ });
1192
+ const existing = existsSync16(ctx.paths.memoriesDir) ? await loadMemoriesFromDir13(ctx.paths.memoriesDir) : [];
1193
+ const existingTopics = new Set(
1194
+ existing.map(({ memory }) => memory.frontmatter.topic).filter((t) => Boolean(t))
1195
+ );
1196
+ const fresh = filterNewDrafts(drafts, existingTopics);
1197
+ const skipped = drafts.length - fresh.length;
1198
+ const created = [];
1199
+ for (const draft of fresh) {
1200
+ let filePath;
1201
+ if (!input.dry_run) filePath = await writeDraft(ctx, draft);
1202
+ created.push({
1203
+ id: draft.frontmatter.id,
1204
+ topic: draft.topic,
1205
+ path: draft.finding.path,
1206
+ rule: draft.finding.ruleId,
1207
+ severity: draft.finding.severity,
1208
+ has_sensor: draft.has_sensor,
1209
+ ...filePath ? { file_path: filePath } : {}
1210
+ });
1211
+ }
1212
+ const notice = input.dry_run ? `Dry run \u2014 ${fresh.length} memory(ies) would be created (status=proposed). Re-run with dry_run=false to write them.` : `Created ${fresh.length} proposed memory(ies). They are NOT validated and their sensors are warn-only \u2014 review with mem_pending and promote with 'haive sensors promote'.`;
1213
+ return {
1214
+ format: input.format,
1215
+ parsed: drafts.length,
1216
+ new: fresh.length,
1217
+ skipped_existing: skipped,
1218
+ dry_run: input.dry_run,
1219
+ created,
1220
+ notice
1221
+ };
1222
+ }
1223
+ async function writeDraft(ctx, draft) {
1224
+ const file = memoryFilePath3(
1225
+ ctx.paths,
1226
+ draft.frontmatter.scope,
1227
+ draft.frontmatter.id,
1228
+ draft.frontmatter.module
1229
+ );
1230
+ await mkdir4(path6.dirname(file), { recursive: true });
1231
+ await writeFile8(file, serializeMemory7({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
1232
+ return file;
1233
+ }
1234
+
1235
+ // src/tools/mem-observe.ts
1236
+ import { mkdir as mkdir5, writeFile as writeFile9 } from "fs/promises";
1237
+ import { existsSync as existsSync17 } from "fs";
1238
+ import path7 from "path";
1239
+ import {
1240
+ buildFrontmatter as buildFrontmatter3,
1241
+ isLikelyGuessable,
1242
+ memoryFilePath as memoryFilePath4,
1243
+ serializeMemory as serializeMemory8
1244
+ } from "@hiveai/core";
1245
+ import { z as z17 } from "zod";
1246
+ var MemObserveInputSchema = {
1247
+ what: z17.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
1248
+ where: z17.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
1249
+ impact: z17.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
1250
+ fix: z17.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
1251
+ scope: z17.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
1252
+ module: z17.string().optional().describe("Module name (required when scope=module)"),
1253
+ tags: z17.array(z17.string()).default([]).describe("Tags for filtering"),
1254
+ author: z17.string().optional().describe("Author handle or email"),
1255
+ force: z17.boolean().default(false).describe(
1165
1256
  "Save even if the observation looks like generic, guessable knowledge. By default, low-specificity observations (things a capable model already knows) are SKIPPED to keep the corpus high-signal \u2014 only unguessable, team-specific discoveries are worth storing."
1166
1257
  )
1167
1258
  };
1168
1259
  async function memObserve(input, ctx) {
1169
- if (!existsSync16(ctx.paths.haiveDir)) {
1260
+ if (!existsSync17(ctx.paths.haiveDir)) {
1170
1261
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1171
1262
  }
1172
1263
  const signalText = [input.what, input.impact, input.fix ?? ""].join(" ");
@@ -1198,26 +1289,26 @@ async function memObserve(input, ctx) {
1198
1289
  lines.push("", `**Fix/workaround:** ${input.fix}`);
1199
1290
  }
1200
1291
  const body = lines.join("\n") + "\n";
1201
- const file = memoryFilePath3(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
1202
- await mkdir4(path6.dirname(file), { recursive: true });
1203
- if (existsSync16(file)) {
1292
+ const file = memoryFilePath4(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
1293
+ await mkdir5(path7.dirname(file), { recursive: true });
1294
+ if (existsSync17(file)) {
1204
1295
  throw new Error(`Memory already exists at ${file}`);
1205
1296
  }
1206
- await writeFile8(file, serializeMemory7({ frontmatter, body }), "utf8");
1297
+ await writeFile9(file, serializeMemory8({ frontmatter, body }), "utf8");
1207
1298
  return { id: frontmatter.id, scope: frontmatter.scope, file_path: file };
1208
1299
  }
1209
1300
 
1210
1301
  // src/tools/mem-session-end.ts
1211
- import { writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
1212
- import { existsSync as existsSync18 } from "fs";
1213
- import path8 from "path";
1302
+ import { writeFile as writeFile11, mkdir as mkdir7 } from "fs/promises";
1303
+ import { existsSync as existsSync19 } from "fs";
1304
+ import path9 from "path";
1214
1305
  import {
1215
1306
  buildFrontmatter as buildFrontmatter4,
1216
- loadMemoriesFromDir as loadMemoriesFromDir13,
1217
- memoryFilePath as memoryFilePath4,
1218
- serializeMemory as serializeMemory8
1307
+ loadMemoriesFromDir as loadMemoriesFromDir14,
1308
+ memoryFilePath as memoryFilePath5,
1309
+ serializeMemory as serializeMemory9
1219
1310
  } from "@hiveai/core";
1220
- import { z as z17 } from "zod";
1311
+ import { z as z18 } from "zod";
1221
1312
 
1222
1313
  // src/session-tracker.ts
1223
1314
  import {
@@ -1225,12 +1316,12 @@ import {
1225
1316
  appendRuntimeJournalEntry,
1226
1317
  loadConfig as loadConfig2
1227
1318
  } from "@hiveai/core";
1228
- import { mkdir as mkdir5, writeFile as writeFile9, rm } from "fs/promises";
1229
- import { existsSync as existsSync17 } from "fs";
1230
- import path7 from "path";
1319
+ import { mkdir as mkdir6, writeFile as writeFile10, rm } from "fs/promises";
1320
+ import { existsSync as existsSync18 } from "fs";
1321
+ import path8 from "path";
1231
1322
  import { execSync } from "child_process";
1232
1323
  function pendingDistillPath(ctx) {
1233
- return path7.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
1324
+ return path8.join(ctx.paths.haiveDir, ".cache", "pending-distill.json");
1234
1325
  }
1235
1326
  var SessionTracker = class {
1236
1327
  events = [];
@@ -1309,7 +1400,7 @@ var SessionTracker = class {
1309
1400
  (e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
1310
1401
  );
1311
1402
  const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
1312
- if (!ranPostTask && isSubstantialSession && existsSync17(this.ctx.paths.haiveDir)) {
1403
+ if (!ranPostTask && isSubstantialSession && existsSync18(this.ctx.paths.haiveDir)) {
1313
1404
  try {
1314
1405
  const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
1315
1406
  const payload = {
@@ -1322,9 +1413,9 @@ var SessionTracker = class {
1322
1413
  ...gitDiff ? { git_diff: gitDiff } : {},
1323
1414
  ...recapId ? { recap_id: recapId } : {}
1324
1415
  };
1325
- const cacheDir = path7.join(this.ctx.paths.haiveDir, ".cache");
1326
- await mkdir5(cacheDir, { recursive: true });
1327
- await writeFile9(
1416
+ const cacheDir = path8.join(this.ctx.paths.haiveDir, ".cache");
1417
+ await mkdir6(cacheDir, { recursive: true });
1418
+ await writeFile10(
1328
1419
  pendingDistillPath(this.ctx),
1329
1420
  JSON.stringify(payload, null, 2) + "\n",
1330
1421
  "utf8"
@@ -1343,7 +1434,7 @@ var SessionTracker = class {
1343
1434
  };
1344
1435
  async function clearPendingDistill(ctx) {
1345
1436
  const p = pendingDistillPath(ctx);
1346
- if (existsSync17(p)) {
1437
+ if (existsSync18(p)) {
1347
1438
  try {
1348
1439
  await rm(p);
1349
1440
  } catch {
@@ -1360,15 +1451,15 @@ function summarizeTools(events) {
1360
1451
 
1361
1452
  // src/tools/mem-session-end.ts
1362
1453
  var MemSessionEndInputSchema = {
1363
- goal: z17.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
1364
- accomplished: z17.string().describe("What was actually done \u2014 bullet list recommended"),
1365
- discoveries: z17.string().default("").describe(
1454
+ goal: z18.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
1455
+ accomplished: z18.string().describe("What was actually done \u2014 bullet list recommended"),
1456
+ discoveries: z18.string().default("").describe(
1366
1457
  "Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
1367
1458
  ),
1368
- files_touched: z17.array(z17.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
1369
- next_steps: z17.string().default("").describe("What should happen next (for the next session or a teammate)"),
1370
- scope: z17.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
1371
- module: z17.string().optional().describe("Module name (required when scope=module)")
1459
+ files_touched: z18.array(z18.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
1460
+ next_steps: z18.string().default("").describe("What should happen next (for the next session or a teammate)"),
1461
+ scope: z18.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
1462
+ module: z18.string().optional().describe("Module name (required when scope=module)")
1372
1463
  };
1373
1464
  function recapTopic(scope, module) {
1374
1465
  return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
@@ -1398,23 +1489,23 @@ ${input.next_steps}`);
1398
1489
  return lines.join("\n");
1399
1490
  }
1400
1491
  async function memSessionEnd(input, ctx) {
1401
- if (!existsSync18(ctx.paths.haiveDir)) {
1492
+ if (!existsSync19(ctx.paths.haiveDir)) {
1402
1493
  throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
1403
1494
  }
1404
1495
  const body = buildBody(input);
1405
1496
  const topic = recapTopic(input.scope, input.module);
1406
1497
  const normalizedFiles = input.files_touched.map((p) => {
1407
- if (!p || !path8.isAbsolute(p)) return p;
1408
- const rel = path8.relative(ctx.paths.root, p);
1498
+ if (!p || !path9.isAbsolute(p)) return p;
1499
+ const rel = path9.relative(ctx.paths.root, p);
1409
1500
  return rel.startsWith("..") ? p : rel;
1410
1501
  });
1411
1502
  const invalidPaths = normalizedFiles.filter(
1412
- (p) => !existsSync18(path8.resolve(ctx.paths.root, p))
1503
+ (p) => !existsSync19(path9.resolve(ctx.paths.root, p))
1413
1504
  );
1414
1505
  if (invalidPaths.length > 0) {
1415
1506
  console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
1416
1507
  }
1417
- const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir13(ctx.paths.memoriesDir) : [];
1508
+ const existing = existsSync19(ctx.paths.memoriesDir) ? await loadMemoriesFromDir14(ctx.paths.memoriesDir) : [];
1418
1509
  const topicMatch = existing.find(
1419
1510
  ({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
1420
1511
  );
@@ -1430,9 +1521,9 @@ async function memSessionEnd(input, ctx) {
1430
1521
  paths: normalizedFiles.length ? normalizedFiles : fm.anchor.paths
1431
1522
  }
1432
1523
  };
1433
- await writeFile10(
1524
+ await writeFile11(
1434
1525
  topicMatch.filePath,
1435
- serializeMemory8({ frontmatter: newFrontmatter, body }),
1526
+ serializeMemory9({ frontmatter: newFrontmatter, body }),
1436
1527
  "utf8"
1437
1528
  );
1438
1529
  await clearPendingDistill(ctx);
@@ -1454,14 +1545,14 @@ async function memSessionEnd(input, ctx) {
1454
1545
  topic,
1455
1546
  status: "validated"
1456
1547
  });
1457
- const file = memoryFilePath4(
1548
+ const file = memoryFilePath5(
1458
1549
  ctx.paths,
1459
1550
  frontmatter.scope,
1460
1551
  frontmatter.id,
1461
1552
  frontmatter.module
1462
1553
  );
1463
- await mkdir6(path8.dirname(file), { recursive: true });
1464
- await writeFile10(file, serializeMemory8({ frontmatter, body }), "utf8");
1554
+ await mkdir7(path9.dirname(file), { recursive: true });
1555
+ await writeFile11(file, serializeMemory9({ frontmatter, body }), "utf8");
1465
1556
  await clearPendingDistill(ctx);
1466
1557
  return {
1467
1558
  id: frontmatter.id,
@@ -1473,8 +1564,8 @@ async function memSessionEnd(input, ctx) {
1473
1564
  }
1474
1565
 
1475
1566
  // src/tools/get-briefing.ts
1476
- import { readFile as readFile4, writeFile as writeFile11 } from "fs/promises";
1477
- import { existsSync as existsSync20 } from "fs";
1567
+ import { readFile as readFile5, writeFile as writeFile12 } from "fs/promises";
1568
+ import { existsSync as existsSync21 } from "fs";
1478
1569
  import {
1479
1570
  allocateBudget,
1480
1571
  computeImpact as computeImpact2,
@@ -1492,13 +1583,13 @@ import {
1492
1583
  literalMatchesAnyToken as literalMatchesAnyToken2,
1493
1584
  loadCodeMap,
1494
1585
  loadConfig as loadConfig3,
1495
- loadMemoriesFromDir as loadMemoriesFromDir14,
1586
+ loadMemoriesFromDir as loadMemoriesFromDir15,
1496
1587
  loadUsageIndex as loadUsageIndex8,
1497
1588
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
1498
1589
  rankMemoriesLexical as rankMemoriesLexical2,
1499
1590
  queryCodeMap,
1500
1591
  resolveBriefingBudget,
1501
- serializeMemory as serializeMemory9,
1592
+ serializeMemory as serializeMemory10,
1502
1593
  specificityScore,
1503
1594
  GUESSABLE_THRESHOLD,
1504
1595
  tokenizeQuery as tokenizeQuery2,
@@ -1506,12 +1597,12 @@ import {
1506
1597
  truncateToTokens,
1507
1598
  writeBriefingMarker
1508
1599
  } from "@hiveai/core";
1509
- import { z as z18 } from "zod";
1600
+ import { z as z19 } from "zod";
1510
1601
 
1511
1602
  // src/tools/briefing-helpers.ts
1512
- import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
1513
- import { existsSync as existsSync19 } from "fs";
1514
- import path9 from "path";
1603
+ import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
1604
+ import { existsSync as existsSync20 } from "fs";
1605
+ import path10 from "path";
1515
1606
  import { isGlobPath, isStackPackSeed, pathsOverlap } from "@hiveai/core";
1516
1607
  function compactSummary(body) {
1517
1608
  for (const line of body.split("\n")) {
@@ -1637,16 +1728,16 @@ async function trySemanticHits(ctx, task, limit) {
1637
1728
  }
1638
1729
  async function loadModuleContexts2(ctx, modules) {
1639
1730
  if (modules.length === 0) return [];
1640
- if (!existsSync19(ctx.paths.modulesContextDir)) return [];
1731
+ if (!existsSync20(ctx.paths.modulesContextDir)) return [];
1641
1732
  const available = new Set(
1642
1733
  (await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
1643
1734
  );
1644
1735
  const out = [];
1645
1736
  for (const m of modules) {
1646
1737
  if (!available.has(m)) continue;
1647
- const file = path9.join(ctx.paths.modulesContextDir, m, "context.md");
1648
- if (existsSync19(file)) {
1649
- out.push({ name: m, content: await readFile3(file, "utf8") });
1738
+ const file = path10.join(ctx.paths.modulesContextDir, m, "context.md");
1739
+ if (existsSync20(file)) {
1740
+ out.push({ name: m, content: await readFile4(file, "utf8") });
1650
1741
  }
1651
1742
  }
1652
1743
  return out;
@@ -1654,35 +1745,35 @@ async function loadModuleContexts2(ctx, modules) {
1654
1745
 
1655
1746
  // src/tools/get-briefing.ts
1656
1747
  var GetBriefingInputSchema = {
1657
- task: z18.string().optional().describe(
1748
+ task: z19.string().optional().describe(
1658
1749
  "What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
1659
1750
  ),
1660
- files: z18.array(z18.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
1661
- max_tokens: z18.number().int().positive().default(8e3).describe(
1751
+ files: z19.array(z19.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
1752
+ max_tokens: z19.number().int().positive().default(8e3).describe(
1662
1753
  "Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
1663
1754
  ),
1664
- max_memories: z18.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
1665
- include_project_context: z18.boolean().default(true),
1666
- include_module_contexts: z18.boolean().default(true),
1667
- semantic: z18.boolean().default(true).describe(
1755
+ max_memories: z19.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
1756
+ include_project_context: z19.boolean().default(true),
1757
+ include_module_contexts: z19.boolean().default(true),
1758
+ semantic: z19.boolean().default(true).describe(
1668
1759
  "Use semantic ranking when a task is provided (requires `haive embeddings index`)."
1669
1760
  ),
1670
- include_stale: z18.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
1671
- track: z18.boolean().default(true).describe("Increment read_count on returned memories"),
1672
- format: z18.enum(["full", "compact", "actions"]).default("full").describe(
1761
+ include_stale: z19.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
1762
+ track: z19.boolean().default(true).describe("Increment read_count on returned memories"),
1763
+ format: z19.enum(["full", "compact", "actions"]).default("full").describe(
1673
1764
  "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."
1674
1765
  ),
1675
- symbols: z18.array(z18.string()).default([]).describe(
1766
+ symbols: z19.array(z19.string()).default([]).describe(
1676
1767
  "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 `haive index code` to have been run."
1677
1768
  ),
1678
- min_semantic_score: z18.number().min(0).max(1).default(0).describe(
1769
+ min_semantic_score: z19.number().min(0).max(1).default(0).describe(
1679
1770
  "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."
1680
1771
  ),
1681
- budget_preset: z18.enum(["quick", "balanced", "deep"]).optional().describe(
1772
+ budget_preset: z19.enum(["quick", "balanced", "deep"]).optional().describe(
1682
1773
  "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."
1683
1774
  )
1684
1775
  };
1685
- var GetBriefingZod = z18.object(GetBriefingInputSchema);
1776
+ var GetBriefingZod = z19.object(GetBriefingInputSchema);
1686
1777
  async function getBriefing(input, ctx) {
1687
1778
  const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
1688
1779
  max_tokens: input.max_tokens,
@@ -1698,8 +1789,8 @@ async function getBriefing(input, ctx) {
1698
1789
  let usage = { version: 1, updated_at: "", by_id: {} };
1699
1790
  let byId = /* @__PURE__ */ new Map();
1700
1791
  let lastSession;
1701
- if (existsSync20(ctx.paths.memoriesDir)) {
1702
- const allLoaded = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
1792
+ if (existsSync21(ctx.paths.memoriesDir)) {
1793
+ const allLoaded = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
1703
1794
  const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
1704
1795
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
1705
1796
  );
@@ -1862,7 +1953,7 @@ async function getBriefing(input, ctx) {
1862
1953
  if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
1863
1954
  const newFm = { ...loaded.memory.frontmatter, status: "validated" };
1864
1955
  try {
1865
- await writeFile11(loaded.filePath, serializeMemory9({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
1956
+ await writeFile12(loaded.filePath, serializeMemory10({ frontmatter: newFm, body: loaded.memory.body }), "utf8");
1866
1957
  m.status = "validated";
1867
1958
  m.confidence = "trusted";
1868
1959
  } catch {
@@ -1870,12 +1961,12 @@ async function getBriefing(input, ctx) {
1870
1961
  }
1871
1962
  }
1872
1963
  }
1873
- const projectContextRaw = input.include_project_context && existsSync20(ctx.paths.projectContext) ? await readFile4(ctx.paths.projectContext, "utf8") : "";
1964
+ const projectContextRaw = input.include_project_context && existsSync21(ctx.paths.projectContext) ? await readFile5(ctx.paths.projectContext, "utf8") : "";
1874
1965
  const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
1875
1966
  const setupWarnings = [];
1876
1967
  let autoContextGenerated = false;
1877
1968
  let projectContext = isTemplateContext ? "" : projectContextRaw;
1878
- if ((isTemplateContext || !existsSync20(ctx.paths.projectContext)) && input.include_project_context) {
1969
+ if ((isTemplateContext || !existsSync21(ctx.paths.projectContext)) && input.include_project_context) {
1879
1970
  const haiveConfig = await loadConfig3(ctx.paths);
1880
1971
  if (haiveConfig.autoContext) {
1881
1972
  const codeMap = await loadCodeMap(ctx.paths);
@@ -2036,8 +2127,8 @@ ${m.content}`).join("\n\n---\n\n"),
2036
2127
  actionRequired.push(extractActionItem(m.id, loaded.memory.body));
2037
2128
  }
2038
2129
  }
2039
- if (existsSync20(ctx.paths.memoriesDir)) {
2040
- const allMems = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
2130
+ if (existsSync21(ctx.paths.memoriesDir)) {
2131
+ const allMems = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
2041
2132
  for (const { memory } of allMems) {
2042
2133
  const fm = memory.frontmatter;
2043
2134
  if (!fm.requires_human_approval) continue;
@@ -2047,9 +2138,9 @@ ${m.content}`).join("\n\n---\n\n"),
2047
2138
  }
2048
2139
  }
2049
2140
  const pendingDistillFile = pendingDistillPath(ctx);
2050
- if (existsSync20(pendingDistillFile)) {
2141
+ if (existsSync21(pendingDistillFile)) {
2051
2142
  try {
2052
- const raw = await readFile4(pendingDistillFile, "utf8");
2143
+ const raw = await readFile5(pendingDistillFile, "utf8");
2053
2144
  const pd = JSON.parse(raw);
2054
2145
  const ageMs = Date.now() - new Date(pd.session_end).getTime();
2055
2146
  const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1e3;
@@ -2076,7 +2167,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2076
2167
  }
2077
2168
  }
2078
2169
  const memoriesEmpty = outputMemories.length === 0;
2079
- const hasMemoriesDir = existsSync20(ctx.paths.memoriesDir);
2170
+ const hasMemoriesDir = existsSync21(ctx.paths.memoriesDir);
2080
2171
  const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
2081
2172
  const hasUnguessableSignal = outputMemories.some(
2082
2173
  (m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
@@ -2123,7 +2214,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2123
2214
  "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."
2124
2215
  );
2125
2216
  }
2126
- if (existsSync20(ctx.paths.haiveDir)) {
2217
+ if (existsSync21(ctx.paths.haiveDir)) {
2127
2218
  await writeBriefingMarker(ctx.paths, {
2128
2219
  sessionId: process.env.HAIVE_SESSION_ID,
2129
2220
  ...input.task ? { task: input.task } : {},
@@ -2174,19 +2265,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
2174
2265
 
2175
2266
  // src/tools/code-map.ts
2176
2267
  import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
2177
- import { z as z19 } from "zod";
2268
+ import { z as z20 } from "zod";
2178
2269
  var CodeMapInputSchema = {
2179
- file: z19.string().optional().describe("Filter to files whose path contains this substring"),
2180
- symbol: z19.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
2181
- paths: z19.array(z19.string()).default([]).describe(
2270
+ file: z20.string().optional().describe("Filter to files whose path contains this substring"),
2271
+ symbol: z20.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
2272
+ paths: z20.array(z20.string()).default([]).describe(
2182
2273
  "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."
2183
2274
  ),
2184
- max_files: z19.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
2185
- max_tokens: z19.number().int().positive().optional().describe(
2275
+ max_files: z20.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
2276
+ max_tokens: z20.number().int().positive().optional().describe(
2186
2277
  "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)."
2187
2278
  )
2188
2279
  };
2189
- var CodeMapInputZod = z19.object(CodeMapInputSchema);
2280
+ var CodeMapInputZod = z20.object(CodeMapInputSchema);
2190
2281
  async function codeMapTool(input, ctx) {
2191
2282
  const map = await loadCodeMap2(ctx.paths);
2192
2283
  if (!map) {
@@ -2255,18 +2346,18 @@ function estimateFileEntryTokens(f) {
2255
2346
  }
2256
2347
 
2257
2348
  // src/tools/mem-diff.ts
2258
- import { existsSync as existsSync21 } from "fs";
2259
- import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
2260
- import { z as z20 } from "zod";
2349
+ import { existsSync as existsSync22 } from "fs";
2350
+ import { loadMemoriesFromDir as loadMemoriesFromDir16 } from "@hiveai/core";
2351
+ import { z as z21 } from "zod";
2261
2352
  var MemDiffInputSchema = {
2262
- id_a: z20.string().min(1).describe("First memory id"),
2263
- id_b: z20.string().min(1).describe("Second memory id")
2353
+ id_a: z21.string().min(1).describe("First memory id"),
2354
+ id_b: z21.string().min(1).describe("Second memory id")
2264
2355
  };
2265
2356
  async function memDiff(input, ctx) {
2266
- if (!existsSync21(ctx.paths.memoriesDir)) {
2357
+ if (!existsSync22(ctx.paths.memoriesDir)) {
2267
2358
  throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
2268
2359
  }
2269
- const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
2360
+ const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
2270
2361
  const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
2271
2362
  const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
2272
2363
  if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
@@ -2300,19 +2391,19 @@ async function memDiff(input, ctx) {
2300
2391
  }
2301
2392
 
2302
2393
  // src/tools/get-recap.ts
2303
- import { existsSync as existsSync22 } from "fs";
2304
- import { loadMemoriesFromDir as loadMemoriesFromDir16 } from "@hiveai/core";
2305
- import { z as z21 } from "zod";
2394
+ import { existsSync as existsSync23 } from "fs";
2395
+ import { loadMemoriesFromDir as loadMemoriesFromDir17 } from "@hiveai/core";
2396
+ import { z as z22 } from "zod";
2306
2397
  var GetRecapInputSchema = {
2307
- scope: z21.enum(["personal", "team", "any"]).default("any").describe(
2398
+ scope: z22.enum(["personal", "team", "any"]).default("any").describe(
2308
2399
  "Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
2309
2400
  )
2310
2401
  };
2311
2402
  async function getRecap(input, ctx) {
2312
- if (!existsSync22(ctx.paths.memoriesDir)) {
2403
+ if (!existsSync23(ctx.paths.memoriesDir)) {
2313
2404
  return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
2314
2405
  }
2315
- const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
2406
+ const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2316
2407
  const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
2317
2408
  (a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
2318
2409
  );
@@ -2336,13 +2427,13 @@ async function getRecap(input, ctx) {
2336
2427
  }
2337
2428
 
2338
2429
  // src/tools/mem-relevant-to.ts
2339
- import { z as z22 } from "zod";
2430
+ import { z as z23 } from "zod";
2340
2431
  var MemRelevantToInputSchema = {
2341
- task: z22.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
2342
- files: z22.array(z22.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
2343
- limit: z22.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
2344
- min_semantic_score: z22.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
2345
- format: z22.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
2432
+ task: z23.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
2433
+ files: z23.array(z23.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
2434
+ limit: z23.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
2435
+ min_semantic_score: z23.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
2436
+ format: z23.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
2346
2437
  };
2347
2438
  async function memRelevantTo(input, ctx) {
2348
2439
  const briefingInput = {
@@ -2372,13 +2463,13 @@ async function memRelevantTo(input, ctx) {
2372
2463
  }
2373
2464
 
2374
2465
  // src/tools/code-search.ts
2375
- import { z as z23 } from "zod";
2466
+ import { z as z24 } from "zod";
2376
2467
  var CodeSearchInputSchema = {
2377
- query: z23.string().min(1).describe(
2468
+ query: z24.string().min(1).describe(
2378
2469
  "Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
2379
2470
  ),
2380
- k: z23.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
2381
- min_score: z23.number().min(0).max(1).default(0.2).describe(
2471
+ k: z24.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
2472
+ min_score: z24.number().min(0).max(1).default(0.2).describe(
2382
2473
  "Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
2383
2474
  )
2384
2475
  };
@@ -2408,27 +2499,27 @@ async function codeSearch(input, ctx) {
2408
2499
  }
2409
2500
 
2410
2501
  // src/tools/why-this-file.ts
2411
- import { existsSync as existsSync23 } from "fs";
2502
+ import { existsSync as existsSync24 } from "fs";
2412
2503
  import { spawn } from "child_process";
2413
- import path10 from "path";
2504
+ import path11 from "path";
2414
2505
  import {
2415
2506
  deriveConfidence as deriveConfidence5,
2416
2507
  getUsage as getUsage7,
2417
2508
  loadCodeMap as loadCodeMap3,
2418
- loadMemoriesFromDir as loadMemoriesFromDir17,
2509
+ loadMemoriesFromDir as loadMemoriesFromDir18,
2419
2510
  loadUsageIndex as loadUsageIndex9,
2420
2511
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
2421
2512
  } from "@hiveai/core";
2422
- import { z as z24 } from "zod";
2513
+ import { z as z25 } from "zod";
2423
2514
  var WhyThisFileInputSchema = {
2424
- path: z24.string().min(1).describe(
2515
+ path: z25.string().min(1).describe(
2425
2516
  "Project-relative path to the file you want context on (e.g. 'packages/mcp/src/tools/mem-save.ts')."
2426
2517
  ),
2427
- git_log_limit: z24.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
2428
- memory_limit: z24.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
2518
+ git_log_limit: z25.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
2519
+ memory_limit: z25.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
2429
2520
  };
2430
2521
  async function whyThisFile(input, ctx) {
2431
- const fileExists = existsSync23(path10.join(ctx.paths.root, input.path));
2522
+ const fileExists = existsSync24(path11.join(ctx.paths.root, input.path));
2432
2523
  const [commits, memories, codeMap] = await Promise.all([
2433
2524
  runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
2434
2525
  collectAnchoredMemories(ctx, input.path, input.memory_limit),
@@ -2469,8 +2560,8 @@ async function whyThisFile(input, ctx) {
2469
2560
  };
2470
2561
  }
2471
2562
  async function collectAnchoredMemories(ctx, filePath, limit) {
2472
- if (!existsSync23(ctx.paths.memoriesDir)) return [];
2473
- const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
2563
+ if (!existsSync24(ctx.paths.memoriesDir)) return [];
2564
+ const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
2474
2565
  const usage = await loadUsageIndex9(ctx.paths);
2475
2566
  const out = [];
2476
2567
  for (const { memory } of all) {
@@ -2524,7 +2615,7 @@ function runCommand(cmd, args, cwd) {
2524
2615
  }
2525
2616
 
2526
2617
  // src/tools/anti-patterns-check.ts
2527
- import { existsSync as existsSync24 } from "fs";
2618
+ import { existsSync as existsSync25 } from "fs";
2528
2619
  import {
2529
2620
  addedLinesFromDiff,
2530
2621
  buildDocFrequency,
@@ -2533,7 +2624,7 @@ import {
2533
2624
  diffHasDistinctiveOverlap,
2534
2625
  getUsage as getUsage8,
2535
2626
  isRetiredMemory as isRetiredMemory2,
2536
- loadMemoriesFromDir as loadMemoriesFromDir18,
2627
+ loadMemoriesFromDir as loadMemoriesFromDir19,
2537
2628
  loadUsageIndex as loadUsageIndex10,
2538
2629
  literalMatchesAnyToken as literalMatchesAnyToken3,
2539
2630
  memoryMatchesAnchorPaths as memoryMatchesAnchorPaths4,
@@ -2541,19 +2632,19 @@ import {
2541
2632
  sensorTargetsFromDiff,
2542
2633
  tokenizeQuery as tokenizeQuery3
2543
2634
  } from "@hiveai/core";
2544
- import { z as z25 } from "zod";
2635
+ import { z as z26 } from "zod";
2545
2636
  var AntiPatternsCheckInputSchema = {
2546
- diff: z25.string().optional().describe(
2637
+ diff: z26.string().optional().describe(
2547
2638
  "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."
2548
2639
  ),
2549
- paths: z25.array(z25.string()).default([]).describe(
2640
+ paths: z26.array(z26.string()).default([]).describe(
2550
2641
  "File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
2551
2642
  ),
2552
- limit: z25.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
2553
- semantic: z25.boolean().default(true).describe(
2643
+ limit: z26.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
2644
+ semantic: z26.boolean().default(true).describe(
2554
2645
  "When true, also use semantic search (requires @hiveai/embeddings + memory index) to find related anti-patterns."
2555
2646
  ),
2556
- min_semantic_score: z25.number().min(0).max(1).default(0.45).describe(
2647
+ min_semantic_score: z26.number().min(0).max(1).default(0.45).describe(
2557
2648
  "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."
2558
2649
  )
2559
2650
  };
@@ -2574,10 +2665,10 @@ async function antiPatternsCheck(input, ctx) {
2574
2665
  notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
2575
2666
  };
2576
2667
  }
2577
- if (!existsSync24(ctx.paths.memoriesDir)) {
2668
+ if (!existsSync25(ctx.paths.memoriesDir)) {
2578
2669
  return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
2579
2670
  }
2580
- const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
2671
+ const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
2581
2672
  const minSemanticScore = input.min_semantic_score ?? 0.45;
2582
2673
  const negative = all.filter(({ memory }) => {
2583
2674
  const t = memory.frontmatter.type;
@@ -2685,19 +2776,19 @@ async function antiPatternsCheck(input, ctx) {
2685
2776
  }
2686
2777
 
2687
2778
  // src/tools/mem-distill.ts
2688
- import { existsSync as existsSync25 } from "fs";
2779
+ import { existsSync as existsSync26 } from "fs";
2689
2780
  import {
2690
- loadMemoriesFromDir as loadMemoriesFromDir19,
2781
+ loadMemoriesFromDir as loadMemoriesFromDir20,
2691
2782
  tokenizeQuery as tokenizeQuery4
2692
2783
  } from "@hiveai/core";
2693
- import { z as z26 } from "zod";
2784
+ import { z as z27 } from "zod";
2694
2785
  var MemDistillInputSchema = {
2695
- since_days: z26.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
2696
- min_cluster: z26.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
2697
- type_filter: z26.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
2786
+ since_days: z27.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
2787
+ min_cluster: z27.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
2788
+ type_filter: z27.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
2698
2789
  "Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
2699
2790
  ),
2700
- scope: z26.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
2791
+ scope: z27.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
2701
2792
  };
2702
2793
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
2703
2794
  var STOP_WORDS = /* @__PURE__ */ new Set([
@@ -2737,11 +2828,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
2737
2828
  "error"
2738
2829
  ]);
2739
2830
  async function memDistill(input, ctx) {
2740
- if (!existsSync25(ctx.paths.memoriesDir)) {
2831
+ if (!existsSync26(ctx.paths.memoriesDir)) {
2741
2832
  return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
2742
2833
  }
2743
2834
  const cutoff = Date.now() - input.since_days * MS_PER_DAY;
2744
- const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
2835
+ const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
2745
2836
  const candidates = all.filter(({ memory }) => {
2746
2837
  const fm = memory.frontmatter;
2747
2838
  if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
@@ -2845,22 +2936,22 @@ function firstHeading(body) {
2845
2936
  }
2846
2937
 
2847
2938
  // src/tools/why-this-decision.ts
2848
- import { existsSync as existsSync26 } from "fs";
2939
+ import { existsSync as existsSync27 } from "fs";
2849
2940
  import { spawn as spawn2 } from "child_process";
2850
2941
  import {
2851
2942
  deriveConfidence as deriveConfidence7,
2852
2943
  getUsage as getUsage9,
2853
- loadMemoriesFromDir as loadMemoriesFromDir20,
2944
+ loadMemoriesFromDir as loadMemoriesFromDir21,
2854
2945
  loadUsageIndex as loadUsageIndex11,
2855
2946
  pathsOverlap as singlePathsOverlap
2856
2947
  } from "@hiveai/core";
2857
- import { z as z27 } from "zod";
2948
+ import { z as z28 } from "zod";
2858
2949
  var WhyThisDecisionInputSchema = {
2859
- id: z27.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
2860
- git_log_limit: z27.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
2950
+ id: z28.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
2951
+ git_log_limit: z28.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
2861
2952
  };
2862
2953
  async function whyThisDecision(input, ctx) {
2863
- if (!existsSync26(ctx.paths.memoriesDir)) {
2954
+ if (!existsSync27(ctx.paths.memoriesDir)) {
2864
2955
  return {
2865
2956
  found: false,
2866
2957
  related: [],
@@ -2869,7 +2960,7 @@ async function whyThisDecision(input, ctx) {
2869
2960
  notice: "No .ai/memories directory."
2870
2961
  };
2871
2962
  }
2872
- const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
2963
+ const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
2873
2964
  const usage = await loadUsageIndex11(ctx.paths);
2874
2965
  const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
2875
2966
  if (!target) {
@@ -2992,28 +3083,28 @@ function runCommand2(cmd, args, cwd) {
2992
3083
  }
2993
3084
 
2994
3085
  // src/tools/mem-conflicts.ts
2995
- import { existsSync as existsSync27 } from "fs";
3086
+ import { existsSync as existsSync28 } from "fs";
2996
3087
  import {
2997
3088
  deriveConfidence as deriveConfidence8,
2998
3089
  getUsage as getUsage10,
2999
- loadMemoriesFromDir as loadMemoriesFromDir21,
3090
+ loadMemoriesFromDir as loadMemoriesFromDir22,
3000
3091
  loadUsageIndex as loadUsageIndex12,
3001
3092
  pathsOverlap as pathsOverlap2,
3002
3093
  tokenizeQuery as tokenizeQuery5
3003
3094
  } from "@hiveai/core";
3004
- import { z as z28 } from "zod";
3095
+ import { z as z29 } from "zod";
3005
3096
  var MemConflictsInputSchema = {
3006
- id: z28.string().min(1).describe("Memory id to check for conflicts."),
3007
- min_score: z28.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
3008
- semantic: z28.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
3097
+ id: z29.string().min(1).describe("Memory id to check for conflicts."),
3098
+ min_score: z29.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
3099
+ semantic: z29.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
3009
3100
  };
3010
3101
  var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
3011
3102
  var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
3012
3103
  async function memConflicts(input, ctx) {
3013
- if (!existsSync27(ctx.paths.memoriesDir)) {
3104
+ if (!existsSync28(ctx.paths.memoriesDir)) {
3014
3105
  return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
3015
3106
  }
3016
- const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
3107
+ const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
3017
3108
  const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
3018
3109
  if (!target) {
3019
3110
  return { found: false, scanned: 0, conflicts: [], notice: `Memory '${input.id}' not found.` };
@@ -3125,17 +3216,17 @@ async function trySemanticSimilarities(ctx, target, others) {
3125
3216
  }
3126
3217
 
3127
3218
  // src/tools/precommit-check.ts
3128
- import { z as z29 } from "zod";
3219
+ import { z as z30 } from "zod";
3129
3220
  var PreCommitCheckInputSchema = {
3130
- diff: z29.string().optional().describe(
3221
+ diff: z30.string().optional().describe(
3131
3222
  "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`."
3132
3223
  ),
3133
- paths: z29.array(z29.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
3134
- block_on: z29.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
3224
+ paths: z30.array(z30.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
3225
+ block_on: z30.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
3135
3226
  "When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
3136
3227
  ),
3137
- semantic: z29.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
3138
- anchored_blocks: z29.boolean().default(false).describe(
3228
+ semantic: z30.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
3229
+ anchored_blocks: z30.boolean().default(false).describe(
3139
3230
  "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."
3140
3231
  )
3141
3232
  };
@@ -3421,17 +3512,17 @@ function repairTargetPathForWarning(warning, paths) {
3421
3512
  }
3422
3513
 
3423
3514
  // src/tools/pattern-detect.ts
3424
- import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
3425
- import { existsSync as existsSync28 } from "fs";
3426
- import path11 from "path";
3515
+ import { mkdir as mkdir8, writeFile as writeFile13 } from "fs/promises";
3516
+ import { existsSync as existsSync29 } from "fs";
3517
+ import path12 from "path";
3427
3518
  import { execSync as execSync2 } from "child_process";
3428
3519
  import {
3429
3520
  buildFrontmatter as buildFrontmatter5,
3430
- memoryFilePath as memoryFilePath5,
3521
+ memoryFilePath as memoryFilePath6,
3431
3522
  readUsageEvents,
3432
- serializeMemory as serializeMemory10
3523
+ serializeMemory as serializeMemory11
3433
3524
  } from "@hiveai/core";
3434
- import { z as z30 } from "zod";
3525
+ import { z as z31 } from "zod";
3435
3526
  var CONFIG_PATTERNS = [
3436
3527
  ".eslintrc",
3437
3528
  "eslint.config",
@@ -3454,12 +3545,12 @@ var CONFIG_PATTERNS = [
3454
3545
  var MAX_DIFF_BYTES = 4096;
3455
3546
  var HOT_FILE_MIN = 3;
3456
3547
  var PatternDetectInputSchema = {
3457
- since_days: z30.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
3458
- dry_run: z30.boolean().default(false).describe("When true, report matches without writing any memory files."),
3459
- scope: z30.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
3548
+ since_days: z31.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
3549
+ dry_run: z31.boolean().default(false).describe("When true, report matches without writing any memory files."),
3550
+ scope: z31.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
3460
3551
  };
3461
3552
  async function patternDetect(input, ctx) {
3462
- if (!existsSync28(ctx.paths.haiveDir)) {
3553
+ if (!existsSync29(ctx.paths.haiveDir)) {
3463
3554
  return {
3464
3555
  scanned_events: 0,
3465
3556
  matches: [],
@@ -3472,13 +3563,13 @@ async function patternDetect(input, ctx) {
3472
3563
  try {
3473
3564
  const changedFiles = gitChangedFiles(ctx.paths.root, input.since_days);
3474
3565
  const configFiles = changedFiles.filter(
3475
- (f) => CONFIG_PATTERNS.some((p) => path11.basename(f.toLowerCase()).includes(p))
3566
+ (f) => CONFIG_PATTERNS.some((p) => path12.basename(f.toLowerCase()).includes(p))
3476
3567
  );
3477
3568
  for (const file of configFiles.slice(0, 5)) {
3478
3569
  const diff = gitFileDiff(ctx.paths.root, file, input.since_days);
3479
3570
  if (!diff) continue;
3480
- const parentDir = path11.basename(path11.dirname(file));
3481
- const baseName = path11.basename(file).replace(/\.[^.]+$/, "");
3571
+ const parentDir = path12.basename(path12.dirname(file));
3572
+ const baseName = path12.basename(file).replace(/\.[^.]+$/, "");
3482
3573
  const slug = `${parentDir}-${baseName}`.replace(/[^a-z0-9]/gi, "-").toLowerCase().slice(0, 40);
3483
3574
  matches.push({
3484
3575
  kind: "config_change",
@@ -3542,7 +3633,7 @@ async function patternDetect(input, ctx) {
3542
3633
  for (const [p, { count, tools }] of pathCounts) {
3543
3634
  if (count < HOT_FILE_MIN) continue;
3544
3635
  if (tools.has("mem_tried") || tools.has("mem_observe")) continue;
3545
- if (CONFIG_PATTERNS.some((cp) => path11.basename(p).includes(cp))) continue;
3636
+ if (CONFIG_PATTERNS.some((cp) => path12.basename(p).includes(cp))) continue;
3546
3637
  const slug = p.replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").slice(0, 40);
3547
3638
  matches.push({
3548
3639
  kind: "hot_file",
@@ -3582,17 +3673,17 @@ async function patternDetect(input, ctx) {
3582
3673
  paths: match.anchor_paths,
3583
3674
  status: "proposed"
3584
3675
  });
3585
- const file = memoryFilePath5(
3676
+ const file = memoryFilePath6(
3586
3677
  ctx.paths,
3587
3678
  fm.scope === "shared" ? "team" : fm.scope,
3588
3679
  fm.id,
3589
3680
  void 0
3590
3681
  );
3591
- if (existsSync28(file)) continue;
3592
- await mkdir7(path11.dirname(file), { recursive: true });
3593
- await writeFile12(
3682
+ if (existsSync29(file)) continue;
3683
+ await mkdir8(path12.dirname(file), { recursive: true });
3684
+ await writeFile13(
3594
3685
  file,
3595
- serializeMemory10({ frontmatter: fm, body: match.proposed_body }),
3686
+ serializeMemory11({ frontmatter: fm, body: match.proposed_body }),
3596
3687
  "utf8"
3597
3688
  );
3598
3689
  savedIds.push(fm.id);
@@ -3634,25 +3725,25 @@ function gitFileDiff(root, file, sinceDays) {
3634
3725
  }
3635
3726
 
3636
3727
  // src/tools/mem-conflict-candidates.ts
3637
- import { existsSync as existsSync29 } from "fs";
3728
+ import { existsSync as existsSync30 } from "fs";
3638
3729
  import {
3639
3730
  findLexicalConflictPairs,
3640
3731
  findTopicStatusConflictPairs,
3641
- loadMemoriesFromDir as loadMemoriesFromDir22
3732
+ loadMemoriesFromDir as loadMemoriesFromDir23
3642
3733
  } from "@hiveai/core";
3643
- import { z as z31 } from "zod";
3734
+ import { z as z32 } from "zod";
3644
3735
  var MemConflictCandidatesInputSchema = {
3645
- since_days: z31.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3646
- types: z31.array(z31.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
3647
- min_jaccard: z31.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
3648
- max_pairs: z31.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
3649
- max_scan: z31.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
3650
- max_topic_pairs: z31.number().int().positive().max(100).default(20).describe(
3736
+ since_days: z32.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
3737
+ types: z32.array(z32.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
3738
+ min_jaccard: z32.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
3739
+ max_pairs: z32.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
3740
+ max_scan: z32.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
3741
+ max_topic_pairs: z32.number().int().positive().max(100).default(20).describe(
3651
3742
  "Cap for extra signal: memories sharing the same topic with validated vs rejected status."
3652
3743
  )
3653
3744
  };
3654
3745
  async function memConflictCandidates(input, ctx) {
3655
- if (!existsSync29(ctx.paths.memoriesDir)) {
3746
+ if (!existsSync30(ctx.paths.memoriesDir)) {
3656
3747
  return {
3657
3748
  pairs: [],
3658
3749
  topic_status_pairs: [],
@@ -3661,7 +3752,7 @@ async function memConflictCandidates(input, ctx) {
3661
3752
  notice: "No .ai/memories directory."
3662
3753
  };
3663
3754
  }
3664
- const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
3755
+ const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
3665
3756
  const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
3666
3757
  sinceDays: input.since_days,
3667
3758
  types: input.types,
@@ -3676,9 +3767,9 @@ async function memConflictCandidates(input, ctx) {
3676
3767
 
3677
3768
  // src/tools/mem-resolve-project.ts
3678
3769
  import { resolveProjectInfo } from "@hiveai/core";
3679
- import { z as z32 } from "zod";
3770
+ import { z as z33 } from "zod";
3680
3771
  var MemResolveProjectInputSchema = {
3681
- cwd: z32.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
3772
+ cwd: z33.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
3682
3773
  };
3683
3774
  async function memResolveProject(input, _ctx) {
3684
3775
  void _ctx;
@@ -3692,10 +3783,10 @@ async function memResolveProject(input, _ctx) {
3692
3783
 
3693
3784
  // src/tools/mem-suggest-topic.ts
3694
3785
  import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
3695
- import { z as z33 } from "zod";
3786
+ import { z as z34 } from "zod";
3696
3787
  var MemSuggestTopicInputSchema = {
3697
3788
  type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
3698
- title: z33.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
3789
+ title: z34.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
3699
3790
  };
3700
3791
  async function memSuggestTopic(input, _ctx) {
3701
3792
  void _ctx;
@@ -3704,19 +3795,19 @@ async function memSuggestTopic(input, _ctx) {
3704
3795
  }
3705
3796
 
3706
3797
  // src/tools/mem-timeline.ts
3707
- import { existsSync as existsSync30 } from "fs";
3708
- import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hiveai/core";
3709
- import { z as z34 } from "zod";
3798
+ import { existsSync as existsSync31 } from "fs";
3799
+ import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir24 } from "@hiveai/core";
3800
+ import { z as z35 } from "zod";
3710
3801
  var MemTimelineInputSchema = {
3711
- memory_id: z34.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
3712
- topic: z34.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
3713
- limit: z34.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
3802
+ memory_id: z35.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
3803
+ topic: z35.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
3804
+ limit: z35.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
3714
3805
  };
3715
3806
  async function memTimeline(input, ctx) {
3716
- if (!existsSync30(ctx.paths.memoriesDir)) {
3807
+ if (!existsSync31(ctx.paths.memoriesDir)) {
3717
3808
  return { entries: [], total: 0, notice: "No .ai/memories directory." };
3718
3809
  }
3719
- const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
3810
+ const all = await loadMemoriesFromDir24(ctx.paths.memoriesDir);
3720
3811
  const { entries, notice } = collectTimelineEntries(all, {
3721
3812
  memoryId: input.memory_id,
3722
3813
  topic: input.topic,
@@ -3727,11 +3818,11 @@ async function memTimeline(input, ctx) {
3727
3818
 
3728
3819
  // src/tools/runtime-journal-append.ts
3729
3820
  import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
3730
- import { z as z35 } from "zod";
3821
+ import { z as z36 } from "zod";
3731
3822
  var RuntimeJournalAppendInputSchema = {
3732
- message: z35.string().min(1).describe("Short line to append to the runtime session journal"),
3733
- kind: z35.enum(["note", "session_end", "mcp"]).default("note"),
3734
- tool: z35.string().optional().describe("When kind=mcp, which tool name (optional)")
3823
+ message: z36.string().min(1).describe("Short line to append to the runtime session journal"),
3824
+ kind: z36.enum(["note", "session_end", "mcp"]).default("note"),
3825
+ tool: z36.string().optional().describe("When kind=mcp, which tool name (optional)")
3735
3826
  };
3736
3827
  async function runtimeJournalAppend(input, ctx) {
3737
3828
  await appendRuntimeJournalEntry2(ctx.paths, {
@@ -3747,9 +3838,9 @@ async function runtimeJournalAppend(input, ctx) {
3747
3838
 
3748
3839
  // src/tools/runtime-journal-tail.ts
3749
3840
  import { readRuntimeJournalTail } from "@hiveai/core";
3750
- import { z as z36 } from "zod";
3841
+ import { z as z37 } from "zod";
3751
3842
  var RuntimeJournalTailInputSchema = {
3752
- limit: z36.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
3843
+ limit: z37.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
3753
3844
  };
3754
3845
  async function runtimeJournalTail(input, ctx) {
3755
3846
  const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
@@ -3760,12 +3851,12 @@ async function runtimeJournalTail(input, ctx) {
3760
3851
  }
3761
3852
 
3762
3853
  // src/prompts/bootstrap-project.ts
3763
- import { z as z37 } from "zod";
3854
+ import { z as z38 } from "zod";
3764
3855
  var BootstrapProjectArgsSchema = {
3765
- module: z37.string().optional().describe(
3856
+ module: z38.string().optional().describe(
3766
3857
  "Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
3767
3858
  ),
3768
- focus: z37.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3859
+ focus: z38.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
3769
3860
  };
3770
3861
  var ROOT_TEMPLATE = `# Project context
3771
3862
 
@@ -3847,10 +3938,10 @@ ${template}\`\`\`
3847
3938
  }
3848
3939
 
3849
3940
  // src/prompts/post-task.ts
3850
- import { z as z38 } from "zod";
3941
+ import { z as z39 } from "zod";
3851
3942
  var PostTaskArgsSchema = {
3852
- task_summary: z38.string().optional().describe("One sentence describing what you just did"),
3853
- files_touched: z38.array(z38.string()).optional().describe("Files you created or modified during the task")
3943
+ task_summary: z39.string().optional().describe("One sentence describing what you just did"),
3944
+ files_touched: z39.array(z39.string()).optional().describe("Files you created or modified during the task")
3854
3945
  };
3855
3946
  function postTaskPrompt(args, ctx) {
3856
3947
  const taskLine = args.task_summary ? `
@@ -3920,7 +4011,7 @@ This creates/updates a single rolling recap that **get_briefing automatically su
3920
4011
 
3921
4012
  Calling \`mem_session_end\` also **clears the pending-distill marker** (if any), confirming that this session's learnings have been properly captured rather than left as an auto-recap skeleton.
3922
4013
 
3923
- ### 7. Verify the git/release exit protocol \u2014 always
4014
+ ### 7. Verify the git/release/pipeline exit protocol \u2014 always
3924
4015
  Run **\`haive enforce finish\`** before your final response.
3925
4016
 
3926
4017
  This executable gate checks the multi-agent git-sync decision:
@@ -3928,11 +4019,12 @@ This executable gate checks the multi-agent git-sync decision:
3928
4019
  - shippable package changes have a lockstep version bump
3929
4020
  - the release tag \`vX.Y.Z\` exists when a version was bumped
3930
4021
  - commits and tags have been pushed
4022
+ - the pushed HEAD's GitHub Actions workflow runs have completed successfully when the repo has a GitHub remote
3931
4023
  - agents never run \`npm publish\` (publication remains human-owned)
3932
4024
 
3933
- If it blocks, fix the reported Git/version/tag/push issue before telling the developer the task is done.
4025
+ If it blocks, fix the reported Git/version/tag/push/pipeline issue before telling the developer the task is done.
3934
4026
 
3935
- When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved. hAIve finish gate passed."
4027
+ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Session recap saved. hAIve finish gate passed; GitHub Actions passed when applicable."
3936
4028
  `;
3937
4029
  return {
3938
4030
  description: "Post-task reflection: capture what you learned before closing the session",
@@ -3946,12 +4038,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
3946
4038
  }
3947
4039
 
3948
4040
  // src/prompts/import-docs.ts
3949
- import { z as z39 } from "zod";
4041
+ import { z as z40 } from "zod";
3950
4042
  var ImportDocsArgsSchema = {
3951
- content: z39.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
3952
- source: z39.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
3953
- scope: z39.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
3954
- dry_run: z39.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
4043
+ content: z40.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
4044
+ source: z40.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
4045
+ scope: z40.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
4046
+ dry_run: z40.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
3955
4047
  };
3956
4048
  function importDocsPrompt(args, ctx) {
3957
4049
  const sourceLine = args.source ? `
@@ -4017,7 +4109,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
4017
4109
  // src/server.ts
4018
4110
  import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
4019
4111
  var SERVER_NAME = "haive";
4020
- var SERVER_VERSION = "0.12.3";
4112
+ var SERVER_VERSION = "0.12.9";
4021
4113
  function jsonResult(data) {
4022
4114
  return {
4023
4115
  content: [
@@ -4060,7 +4152,8 @@ var MAINTENANCE_PROFILE_TOOLS = [
4060
4152
  "mem_distill",
4061
4153
  "mem_timeline",
4062
4154
  "mem_conflict_candidates",
4063
- "mem_feedback"
4155
+ "mem_feedback",
4156
+ "ingest_findings"
4064
4157
  ];
4065
4158
  var EXPERIMENTAL_PROFILE_TOOLS = [
4066
4159
  ...MAINTENANCE_PROFILE_TOOLS,
@@ -4094,7 +4187,8 @@ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
4094
4187
  "mem_delete",
4095
4188
  "mem_feedback",
4096
4189
  "runtime_journal_append",
4097
- "pattern_detect"
4190
+ "pattern_detect",
4191
+ "ingest_findings"
4098
4192
  ]);
4099
4193
  function createHaiveServer(options = {}) {
4100
4194
  const context = createContext(options);
@@ -4219,6 +4313,36 @@ function createHaiveServer(options = {}) {
4219
4313
  return jsonResult(await memTried(input, context));
4220
4314
  }
4221
4315
  );
4316
+ registerTool(
4317
+ "ingest_findings",
4318
+ [
4319
+ "Turn scanner findings (SonarQube / SARIF) into proposed, anchored memories with sensors.",
4320
+ "",
4321
+ "USE THIS to seed hAIve from your existing quality tooling: each real defect a scanner",
4322
+ "found becomes a `gotcha`/`convention` memory anchored to the file, pre-filled with a",
4323
+ "conservative `warn` sensor \u2014 so the next agent is steered away from it before re-writing it.",
4324
+ "This closes the review\u2194memory loop and kills the cold-start problem.",
4325
+ "",
4326
+ "SAFETY: drafts are status=proposed and sensors are warn-only + autogen. This tool NEVER",
4327
+ "auto-validates and NEVER auto-blocks. A human reviews (mem_pending) and promotes the sensor.",
4328
+ "",
4329
+ "PARAMETERS:",
4330
+ " format \u2014 'sarif' (ESLint/Semgrep/CodeQL) | 'sonar' (SonarQube issues JSON)",
4331
+ " report_path \u2014 project-relative path to the report file (OR pass `report` inline)",
4332
+ " report \u2014 inline JSON content (OR pass `report_path`)",
4333
+ " type \u2014 gotcha (default) | convention",
4334
+ " scope \u2014 team (default) | personal | module",
4335
+ " min_severity \u2014 drop findings below this severity",
4336
+ " dry_run \u2014 preview what would be created without writing",
4337
+ "",
4338
+ "RETURNS: { format, parsed, new, skipped_existing, created[], notice }"
4339
+ ].join("\n"),
4340
+ IngestFindingsInputSchema,
4341
+ async (input) => {
4342
+ tracker.record("ingest_findings", `${input.format}:${input.report_path ?? "inline"}`);
4343
+ return jsonResult(await ingestFindings(input, context));
4344
+ }
4345
+ );
4222
4346
  registerTool(
4223
4347
  "mem_observe",
4224
4348
  [