@riconext/hermes-repo 1.2.1 → 1.2.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 17d33f3: 增加 flush 调用 LLM 时的详细 debug 日志,便于检查请求输入、原始响应和标准化后的知识文件结果。
8
+
9
+ ## 1.2.2
10
+
11
+ ### Patch Changes
12
+
13
+ - a8aff7f: 记录 flush 执行过程到 debug 日志,并在写入 MEMORY.md 前校验知识文件链接是否真实存在。
14
+
3
15
  ## 1.2.1
4
16
 
5
17
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -62,6 +62,16 @@ function debugLog(enabled, phase, message) {
62
62
  console.error(line);
63
63
  writeToLogFile(line);
64
64
  }
65
+ function debugLogBlock(enabled, phase, label, content) {
66
+ if (!enabled) {
67
+ return;
68
+ }
69
+ debugLog(true, phase, `${label} BEGIN`);
70
+ for (const line of content.split(/\r?\n/)) {
71
+ debugLog(true, phase, `| ${line}`);
72
+ }
73
+ debugLog(true, phase, `${label} END`);
74
+ }
65
75
  function debugFromContext(ctx, phase, message) {
66
76
  debugLog(ctx?.config.debug === true, phase, message);
67
77
  }
@@ -1423,7 +1433,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
1423
1433
  currentMemoryMd
1424
1434
  };
1425
1435
  }
1426
- async function callLlmConsolidate(input2, llmConfig) {
1436
+ async function callLlmConsolidate(input2, llmConfig, debug = false) {
1427
1437
  if (!llmConfig.enabled) {
1428
1438
  throw new Error(
1429
1439
  "LLM \u672A\u542F\u7528\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.enabled = true"
@@ -1436,6 +1446,23 @@ async function callLlmConsolidate(input2, llmConfig) {
1436
1446
  }
1437
1447
  const url = `${llmConfig.baseUrl.replace(/\/$/, "")}/chat/completions`;
1438
1448
  const userContent = formatUserMessage(input2);
1449
+ debugLog(
1450
+ debug,
1451
+ "llm",
1452
+ `request: provider=${llmConfig.provider}, model=${llmConfig.model}, baseUrl=${llmConfig.baseUrl}, pendingSessions=${input2.pendingSessions.length}, existingKnowledge=${input2.existingKnowledge.length}, currentMemoryChars=${input2.currentMemoryMd?.length ?? 0}`
1453
+ );
1454
+ debugLogBlock(debug, "llm", "system prompt", CONSOLIDATE_SYSTEM_PROMPT);
1455
+ debugLogBlock(debug, "llm", "user input", userContent);
1456
+ const requestBody = {
1457
+ model: llmConfig.model,
1458
+ response_format: { type: "json_object" },
1459
+ messages: [
1460
+ { role: "system", content: CONSOLIDATE_SYSTEM_PROMPT },
1461
+ { role: "user", content: userContent }
1462
+ ],
1463
+ temperature: 0.2
1464
+ };
1465
+ debugLogBlock(debug, "llm", "request body", JSON.stringify(requestBody, null, 2));
1439
1466
  const controller = new AbortController();
1440
1467
  const timeout = setTimeout(() => controller.abort(), 12e4);
1441
1468
  try {
@@ -1445,35 +1472,58 @@ async function callLlmConsolidate(input2, llmConfig) {
1445
1472
  "Content-Type": "application/json",
1446
1473
  Authorization: `Bearer ${llmConfig.apiKey}`
1447
1474
  },
1448
- body: JSON.stringify({
1449
- model: llmConfig.model,
1450
- response_format: { type: "json_object" },
1451
- messages: [
1452
- { role: "system", content: CONSOLIDATE_SYSTEM_PROMPT },
1453
- { role: "user", content: userContent }
1454
- ],
1455
- temperature: 0.2
1456
- }),
1475
+ body: JSON.stringify(requestBody),
1457
1476
  signal: controller.signal
1458
1477
  });
1478
+ debugLog(debug, "llm", `response status: ${res.status} ${res.statusText}`);
1459
1479
  if (!res.ok) {
1460
1480
  const errBody = await res.text().catch(() => "");
1481
+ debugLogBlock(debug, "llm", "error response body", errBody);
1461
1482
  throw new Error(
1462
1483
  `LLM API \u9519\u8BEF (${res.status}): ${errBody.slice(0, 300)}`
1463
1484
  );
1464
1485
  }
1465
1486
  const data = await res.json();
1487
+ debugLogBlock(debug, "llm", "response json", JSON.stringify(data, null, 2));
1466
1488
  const rawContent = data.choices?.[0]?.message?.content;
1467
1489
  if (!rawContent) {
1468
1490
  throw new Error("LLM \u8FD4\u56DE\u5185\u5BB9\u4E3A\u7A7A");
1469
1491
  }
1492
+ debugLogBlock(debug, "llm", "raw message content", rawContent);
1470
1493
  let parsed;
1471
1494
  try {
1472
1495
  parsed = JSON.parse(rawContent);
1473
1496
  } catch {
1474
1497
  throw new Error("LLM \u8FD4\u56DE\u5185\u5BB9\u4E0D\u662F\u5408\u6CD5 JSON");
1475
1498
  }
1476
- return validateAndNormalizeLlmResult(parsed);
1499
+ debugLogBlock(debug, "llm", "parsed content", JSON.stringify(parsed, null, 2));
1500
+ const normalized = validateAndNormalizeLlmResult(parsed);
1501
+ debugLog(
1502
+ debug,
1503
+ "llm",
1504
+ `normalized: knowledgeFiles=${normalized.knowledgeFiles.length}, memoryChars=${normalized.memoryMd.length}, skippedSessions=${normalized.skippedSessions.length}`
1505
+ );
1506
+ debugLogBlock(
1507
+ debug,
1508
+ "llm",
1509
+ "normalized knowledgeFiles",
1510
+ JSON.stringify(normalized.knowledgeFiles, null, 2)
1511
+ );
1512
+ debugLogBlock(debug, "llm", "normalized memoryMd", normalized.memoryMd);
1513
+ debugLogBlock(
1514
+ debug,
1515
+ "llm",
1516
+ "normalized skippedSessions",
1517
+ JSON.stringify(normalized.skippedSessions, null, 2)
1518
+ );
1519
+ return normalized;
1520
+ } catch (err) {
1521
+ debugLog(
1522
+ debug,
1523
+ "llm",
1524
+ `error: ${err instanceof Error ? err.message : String(err)}`
1525
+ );
1526
+ throw err;
1477
1527
  } finally {
1478
1528
  clearTimeout(timeout);
1479
1529
  }
@@ -1537,6 +1587,13 @@ function isSkippedEntry(raw) {
1537
1587
  // src/consolidate/writeKnowledge.ts
1538
1588
  import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
1539
1589
  import { dirname as dirname4 } from "path";
1590
+ var KNOWLEDGE_LINK_PREFIXES = [
1591
+ "rules/",
1592
+ "domains/",
1593
+ "workflows/",
1594
+ "decisions/",
1595
+ "incidents/"
1596
+ ];
1540
1597
  function writeKnowledgeFiles(repoRoot, files) {
1541
1598
  const result = {
1542
1599
  created: [],
@@ -1580,6 +1637,38 @@ function writeMemoryMd(repoRoot, memoryMd) {
1580
1637
  writeFileSync5(memoryPathAbs, `${memoryMd}
1581
1638
  `, "utf8");
1582
1639
  }
1640
+ function assertMemoryKnowledgeLinksExist(repoRoot, memoryMd) {
1641
+ const missing = findMissingKnowledgeLinks(repoRoot, memoryMd);
1642
+ if (missing.length > 0) {
1643
+ throw new Error(
1644
+ `MEMORY.md \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684\u77E5\u8BC6\u6587\u4EF6: ${missing.join(", ")}`
1645
+ );
1646
+ }
1647
+ }
1648
+ function findMissingKnowledgeLinks(repoRoot, memoryMd) {
1649
+ const links = extractKnowledgeLinks(memoryMd);
1650
+ return links.filter((link) => !existsSync7(memoryPath(repoRoot, link)));
1651
+ }
1652
+ function extractKnowledgeLinks(memoryMd) {
1653
+ const links = /* @__PURE__ */ new Set();
1654
+ const markdownLink = /\[[^\]]+\]\(([^)]+)\)/g;
1655
+ let match;
1656
+ while ((match = markdownLink.exec(memoryMd)) !== null) {
1657
+ const normalized = normalizeKnowledgeLink(match[1]);
1658
+ if (normalized) {
1659
+ links.add(normalized);
1660
+ }
1661
+ }
1662
+ return [...links];
1663
+ }
1664
+ function normalizeKnowledgeLink(rawLink) {
1665
+ const withoutAnchor = rawLink.split("#")[0].trim();
1666
+ const withoutDotSlash = withoutAnchor.startsWith("./") ? withoutAnchor.slice(2) : withoutAnchor;
1667
+ if (withoutDotSlash.includes("://") || withoutDotSlash.startsWith("/") || withoutDotSlash.includes("..") || !withoutDotSlash.endsWith(".md")) {
1668
+ return null;
1669
+ }
1670
+ return KNOWLEDGE_LINK_PREFIXES.some((prefix) => withoutDotSlash.startsWith(prefix)) ? withoutDotSlash : null;
1671
+ }
1583
1672
  function serializeKnowledgeFile(frontmatter, body) {
1584
1673
  const lines = ["---"];
1585
1674
  const fieldOrder = ["title", "domain", "type", "status", "confidence", "lastReviewed"];
@@ -1666,10 +1755,17 @@ function archiveDoneSessions(repoRoot, sessions, autoArchiveDays = 30) {
1666
1755
  // src/consolidate/runConsolidate.ts
1667
1756
  async function runConsolidate(opts) {
1668
1757
  const { repoRoot, config, force, dryRun, debug } = opts;
1758
+ debugLog(
1759
+ debug === true,
1760
+ "consolidate",
1761
+ `start: force=${force === true}, dryRun=${dryRun === true}`
1762
+ );
1669
1763
  writeConsolidateLock(repoRoot);
1764
+ debugLog(debug === true, "consolidate", "lock acquired");
1670
1765
  try {
1671
1766
  const llmConfig = config.llm;
1672
1767
  if (!llmConfig.enabled) {
1768
+ debugLog(debug === true, "consolidate", "skip: llm not enabled");
1673
1769
  return {
1674
1770
  ran: false,
1675
1771
  reason: "llm-not-enabled",
@@ -1688,6 +1784,7 @@ async function runConsolidate(opts) {
1688
1784
  `\u626B\u63CF\u5230 ${allSessions.length} \u4E2A session\uFF0C\u5176\u4E2D ${pendingSessions.length} \u4E2A\u5F85\u5904\u7406`
1689
1785
  );
1690
1786
  if (pendingSessions.length === 0 && !force) {
1787
+ debugLog(debug === true, "consolidate", "skip: no pending sessions");
1691
1788
  return {
1692
1789
  ran: false,
1693
1790
  reason: "no-pending-sessions",
@@ -1699,6 +1796,11 @@ async function runConsolidate(opts) {
1699
1796
  };
1700
1797
  }
1701
1798
  if (dryRun) {
1799
+ debugLog(
1800
+ debug === true,
1801
+ "consolidate",
1802
+ `dry-run: would process ${pendingSessions.length} session(s)`
1803
+ );
1702
1804
  return {
1703
1805
  ran: true,
1704
1806
  reason: "dry-run",
@@ -1710,10 +1812,14 @@ async function runConsolidate(opts) {
1710
1812
  };
1711
1813
  }
1712
1814
  const llmInput = buildLlmConsolidateInput(repoRoot, pendingSessions);
1713
- debugLog(debug === true, "consolidate", `LLM \u8F93\u5165: ${llmInput.pendingSessions.length} sessions, ${llmInput.existingKnowledge.length} existing knowledge`);
1815
+ debugLog(
1816
+ debug === true,
1817
+ "consolidate",
1818
+ `LLM \u8F93\u5165: ${llmInput.pendingSessions.length} sessions, ${llmInput.existingKnowledge.length} existing knowledge`
1819
+ );
1714
1820
  let llmResult;
1715
1821
  try {
1716
- llmResult = await callLlmConsolidate(llmInput, llmConfig);
1822
+ llmResult = await callLlmConsolidate(llmInput, llmConfig, debug === true);
1717
1823
  } catch (err) {
1718
1824
  console.error(`[consolidate] LLM \u8C03\u7528\u5931\u8D25: ${err.message}`);
1719
1825
  throw err;
@@ -1723,8 +1829,33 @@ async function runConsolidate(opts) {
1723
1829
  "consolidate",
1724
1830
  `LLM \u8FD4\u56DE: ${llmResult.knowledgeFiles.length} knowledge files, ${llmResult.skippedSessions.length} skipped`
1725
1831
  );
1832
+ debugLog(debug === true, "consolidate", "writing knowledge files");
1726
1833
  const writeResult = writeKnowledgeFiles(repoRoot, llmResult.knowledgeFiles);
1834
+ if (writeResult.failed.length > 0) {
1835
+ debugLog(
1836
+ debug === true,
1837
+ "consolidate",
1838
+ `write failed: ${writeResult.failed.join(", ")}`
1839
+ );
1840
+ throw new Error(
1841
+ `\u77E5\u8BC6\u6587\u4EF6\u5199\u5165\u5931\u8D25: ${writeResult.failed.join(", ")}`
1842
+ );
1843
+ }
1844
+ debugLog(
1845
+ debug === true,
1846
+ "consolidate",
1847
+ `knowledge files written: created=${writeResult.created.length}, updated=${writeResult.updated.length}`
1848
+ );
1849
+ debugLog(debug === true, "consolidate", "validating MEMORY.md links");
1850
+ assertMemoryKnowledgeLinksExist(repoRoot, llmResult.memoryMd);
1851
+ debugLog(debug === true, "consolidate", "MEMORY.md links ok");
1852
+ debugLog(debug === true, "consolidate", "writing MEMORY.md");
1727
1853
  writeMemoryMd(repoRoot, llmResult.memoryMd);
1854
+ debugLog(
1855
+ debug === true,
1856
+ "consolidate",
1857
+ `marking sessions done: ${pendingSessions.length} processed, ${llmResult.skippedSessions.length} skipped`
1858
+ );
1728
1859
  const processedSessionIds = /* @__PURE__ */ new Set();
1729
1860
  for (const s of pendingSessions) {
1730
1861
  markSessionConsolidated(repoRoot, s.sessionId);
@@ -1736,6 +1867,7 @@ async function runConsolidate(opts) {
1736
1867
  }
1737
1868
  const prevState = readConsolidateState(repoRoot);
1738
1869
  const newDomains = extractDomainsFromResults(llmResult.knowledgeFiles);
1870
+ debugLog(debug === true, "consolidate", "updating consolidate state");
1739
1871
  writeConsolidateState(repoRoot, {
1740
1872
  version: 2,
1741
1873
  lastConsolidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1759,6 +1891,12 @@ async function runConsolidate(opts) {
1759
1891
  allSessions,
1760
1892
  config.consolidate.autoArchiveDays
1761
1893
  );
1894
+ debugLog(debug === true, "consolidate", `archived sessions: ${archived}`);
1895
+ debugLog(
1896
+ debug === true,
1897
+ "consolidate",
1898
+ `done: sessions=${pendingSessions.length}, created=${writeResult.created.length}, updated=${writeResult.updated.length}, skipped=${llmResult.skippedSessions.length}, archived=${archived}`
1899
+ );
1762
1900
  return {
1763
1901
  ran: true,
1764
1902
  sessionsProcessed: pendingSessions.length,
@@ -1769,6 +1907,7 @@ async function runConsolidate(opts) {
1769
1907
  };
1770
1908
  } finally {
1771
1909
  releaseConsolidateLock(repoRoot);
1910
+ debugLog(debug === true, "consolidate", "lock released");
1772
1911
  }
1773
1912
  }
1774
1913
  function extractDomainsFromResults(files) {
@@ -2421,12 +2560,25 @@ function runCaptureCommand(opts) {
2421
2560
 
2422
2561
  // src/commands/flush.ts
2423
2562
  async function runFlushCommandCli(opts) {
2563
+ const ctx = loadRepoContext(opts.cwd);
2564
+ const debug = ctx?.config.debug === true;
2565
+ configureDebugLogging(ctx?.repoRoot ?? null, debug);
2566
+ debugLog(
2567
+ debug,
2568
+ "flush",
2569
+ `start: force=${opts.force === true}, dryRun=${opts.dryRun === true}`
2570
+ );
2424
2571
  try {
2425
2572
  const result = await runFlushCommand({
2426
2573
  cwd: opts.cwd,
2427
2574
  force: opts.force,
2428
2575
  dryRun: opts.dryRun
2429
2576
  });
2577
+ debugLog(
2578
+ debug,
2579
+ "flush",
2580
+ `result: ran=${result.ran}, reason=${result.reason ?? "ok"}, sessions=${result.sessionsProcessed}, created=${result.knowledgeCreated}, updated=${result.knowledgeUpdated}, skipped=${result.skippedCount}, archived=${result.archived}`
2581
+ );
2430
2582
  if (result.ran) {
2431
2583
  if (result.reason === "dry-run") {
2432
2584
  console.error(
@@ -2466,6 +2618,11 @@ async function runFlushCommandCli(opts) {
2466
2618
  }
2467
2619
  hookExit(0, opts.strict);
2468
2620
  } catch (err) {
2621
+ debugLog(
2622
+ debug,
2623
+ "flush",
2624
+ `error: ${err instanceof Error ? err.message : String(err)}`
2625
+ );
2469
2626
  console.error(
2470
2627
  `hermes-repo flush: ${err instanceof Error ? err.message : String(err)}`
2471
2628
  );