@riconext/hermes-repo 1.2.3 → 1.2.5

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/cli.js CHANGED
@@ -5,7 +5,7 @@ import { Command } from "commander";
5
5
 
6
6
  // src/config/debugLog.ts
7
7
  import { appendFileSync, mkdirSync } from "fs";
8
- import { dirname } from "path";
8
+ import { join as join2 } from "path";
9
9
 
10
10
  // src/init/paths.ts
11
11
  import { join } from "path";
@@ -34,33 +34,52 @@ function memoryPath(root, ...segments) {
34
34
  }
35
35
 
36
36
  // src/config/debugLog.ts
37
- var DEBUG_LOG_FILE = "hermes-debug.log";
38
- var logFilePath = null;
37
+ var DEBUG_LOG_DIR = "logs";
38
+ var DEBUG_LOG_FILES = {
39
+ capture: "capture.log",
40
+ flush: "flush.log",
41
+ consolidate: "consolidate.log"
42
+ };
43
+ var logDirPath = null;
39
44
  function configureDebugLogging(repoRoot, enabled) {
40
45
  if (!enabled || !repoRoot) {
41
- logFilePath = null;
46
+ logDirPath = null;
42
47
  return;
43
48
  }
44
- logFilePath = memoryPath(repoRoot, DEBUG_LOG_FILE);
49
+ logDirPath = memoryPath(repoRoot, DEBUG_LOG_DIR);
45
50
  }
46
51
  function formatLine(phase, message) {
47
52
  return `${(/* @__PURE__ */ new Date()).toISOString()} hermes-repo [${phase}] ${message}`;
48
53
  }
49
- function writeToLogFile(line) {
50
- if (!logFilePath) {
54
+ function writeToLogFile(phase, line) {
55
+ if (!logDirPath) {
51
56
  return;
52
57
  }
53
- mkdirSync(dirname(logFilePath), { recursive: true });
54
- appendFileSync(logFilePath, `${line}
58
+ mkdirSync(logDirPath, { recursive: true });
59
+ appendFileSync(join2(logDirPath, logFileNameForPhase(phase)), `${line}
55
60
  `, "utf8");
56
61
  }
62
+ function logFileNameForPhase(phase) {
63
+ switch (phase) {
64
+ case "capture":
65
+ case "capture-llm":
66
+ return DEBUG_LOG_FILES.capture;
67
+ case "flush":
68
+ return DEBUG_LOG_FILES.flush;
69
+ case "consolidate":
70
+ case "llm":
71
+ return DEBUG_LOG_FILES.consolidate;
72
+ default:
73
+ return `${phase.replace(/[^a-z0-9_-]/gi, "-") || "debug"}.log`;
74
+ }
75
+ }
57
76
  function debugLog(enabled, phase, message) {
58
77
  if (!enabled) {
59
78
  return;
60
79
  }
61
80
  const line = formatLine(phase, message);
62
81
  console.error(line);
63
- writeToLogFile(line);
82
+ writeToLogFile(phase, line);
64
83
  }
65
84
  function debugLogBlock(enabled, phase, label, content) {
66
85
  if (!enabled) {
@@ -78,19 +97,19 @@ function debugFromContext(ctx, phase, message) {
78
97
 
79
98
  // src/config/readConfig.ts
80
99
  import { readFileSync } from "fs";
81
- import { join as join3 } from "path";
100
+ import { join as join4 } from "path";
82
101
 
83
102
  // src/config/findRepoRoot.ts
84
103
  import { existsSync } from "fs";
85
- import { dirname as dirname2, join as join2, resolve } from "path";
86
- var CONFIG_REL = join2(".memory", "config.json");
104
+ import { dirname, join as join3, resolve } from "path";
105
+ var CONFIG_REL = join3(".memory", "config.json");
87
106
  function findRepoRoot(startDir) {
88
107
  let dir = resolve(startDir ?? process.cwd());
89
108
  while (true) {
90
- if (existsSync(join2(dir, CONFIG_REL))) {
109
+ if (existsSync(join3(dir, CONFIG_REL))) {
91
110
  return dir;
92
111
  }
93
- const parent = dirname2(dir);
112
+ const parent = dirname(dir);
94
113
  if (parent === dir) {
95
114
  return null;
96
115
  }
@@ -129,7 +148,7 @@ function parseConsolidateConfig(raw) {
129
148
  };
130
149
  }
131
150
  function readConfigAtRepo(repoRoot) {
132
- const configPath = join3(repoRoot, ".memory", "config.json");
151
+ const configPath = join4(repoRoot, ".memory", "config.json");
133
152
  try {
134
153
  const raw = JSON.parse(readFileSync(configPath, "utf8"));
135
154
  const version = raw.version;
@@ -192,7 +211,7 @@ function loadRepoContext(cwd) {
192
211
 
193
212
  // src/capture/runLlmJob.ts
194
213
  import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync3 } from "fs";
195
- import { join as join6 } from "path";
214
+ import { join as join7 } from "path";
196
215
 
197
216
  // src/config/llmConfig.ts
198
217
  function isLlmAvailable(cfg) {
@@ -359,13 +378,13 @@ import {
359
378
  rmSync,
360
379
  writeFileSync
361
380
  } from "fs";
362
- import { dirname as dirname3, join as join4 } from "path";
381
+ import { dirname as dirname2, join as join5 } from "path";
363
382
  import { fileURLToPath } from "url";
364
383
  function pendingDir(repoRoot) {
365
384
  return memoryPath(repoRoot, "captures", "pending");
366
385
  }
367
386
  function cliPath() {
368
- return join4(dirname3(fileURLToPath(import.meta.url)), "..", "cli.js");
387
+ return join5(dirname2(fileURLToPath(import.meta.url)), "..", "cli.js");
369
388
  }
370
389
  function makeJobId(sessionId) {
371
390
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 32);
@@ -382,10 +401,10 @@ function removeStaleJobsForSession(repoRoot, sessionId) {
382
401
  }
383
402
  try {
384
403
  const raw = JSON.parse(
385
- readFileSync3(join4(dir, name), "utf8")
404
+ readFileSync3(join5(dir, name), "utf8")
386
405
  );
387
406
  if (raw.sessionId === sessionId) {
388
- rmSync(join4(dir, name), { force: true });
407
+ rmSync(join5(dir, name), { force: true });
389
408
  }
390
409
  } catch {
391
410
  }
@@ -406,7 +425,7 @@ function enqueueLlmJob(opts) {
406
425
  enqueuedAt: (/* @__PURE__ */ new Date()).toISOString()
407
426
  };
408
427
  writeFileSync(
409
- join4(dir, `${jobId}.json`),
428
+ join5(dir, `${jobId}.json`),
410
429
  `${JSON.stringify(payload, null, 2)}
411
430
  `,
412
431
  "utf8"
@@ -425,7 +444,7 @@ function enqueueLlmJob(opts) {
425
444
  return true;
426
445
  }
427
446
  function readLlmJob(repoRoot, jobId) {
428
- const path = join4(pendingDir(repoRoot), `${jobId}.json`);
447
+ const path = join5(pendingDir(repoRoot), `${jobId}.json`);
429
448
  if (!existsSync2(path)) {
430
449
  return null;
431
450
  }
@@ -436,7 +455,7 @@ function readLlmJob(repoRoot, jobId) {
436
455
  }
437
456
  }
438
457
  function deleteLlmJob(repoRoot, jobId) {
439
- const path = join4(pendingDir(repoRoot), `${jobId}.json`);
458
+ const path = join5(pendingDir(repoRoot), `${jobId}.json`);
440
459
  if (existsSync2(path)) {
441
460
  rmSync(path, { force: true });
442
461
  }
@@ -453,7 +472,7 @@ function listPendingJobs(repoRoot) {
453
472
  }
454
473
  try {
455
474
  jobs.push(
456
- JSON.parse(readFileSync3(join4(dir, name), "utf8"))
475
+ JSON.parse(readFileSync3(join5(dir, name), "utf8"))
457
476
  );
458
477
  } catch {
459
478
  }
@@ -684,7 +703,7 @@ async function llmFormat(session, assistant, llm) {
684
703
 
685
704
  // src/capture/writeCapture.ts
686
705
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
687
- import { join as join5 } from "path";
706
+ import { join as join6 } from "path";
688
707
  function isoNow() {
689
708
  return (/* @__PURE__ */ new Date()).toISOString();
690
709
  }
@@ -735,7 +754,7 @@ function renderCaptureSection(formatted, index) {
735
754
  function resolveSessionFile(repoRoot, sessionId) {
736
755
  const filename = `session-${sessionId}.md`;
737
756
  const absolutePath = memoryPath(repoRoot, "captures", "raw", filename);
738
- const relativePath = join5(".memory", "captures", "raw", filename);
757
+ const relativePath = join6(".memory", "captures", "raw", filename);
739
758
  return { absolutePath, relativePath, exists: existsSync3(absolutePath) };
740
759
  }
741
760
  function renderCaptureMarkdown(formatted, date) {
@@ -759,7 +778,7 @@ function renderCaptureMarkdown(formatted, date) {
759
778
  function appendCaptureToSession(repoRoot, formatted) {
760
779
  const { absolutePath, relativePath, exists } = resolveSessionFile(repoRoot, formatted.sessionId);
761
780
  const filename = `session-${formatted.sessionId}.md`;
762
- mkdirSync3(join5(absolutePath, ".."), { recursive: true });
781
+ mkdirSync3(join6(absolutePath, ".."), { recursive: true });
763
782
  const now = isoNow();
764
783
  if (!exists) {
765
784
  const fm = {
@@ -856,7 +875,7 @@ function markSessionConsolidated(repoRoot, sessionId) {
856
875
 
857
876
  // src/capture/runLlmJob.ts
858
877
  function captureAlreadyUpgraded(repoRoot, captureFile) {
859
- const path = join6(repoRoot, captureFile);
878
+ const path = join7(repoRoot, captureFile);
860
879
  if (!existsSync4(path)) {
861
880
  return false;
862
881
  }
@@ -881,7 +900,7 @@ async function runLlmJob(repoRoot, job, debug) {
881
900
  debugLog(debug === true, "capture-llm", `llm job failed: ${job.jobId}`);
882
901
  return { ok: false, reason: "llm format failed" };
883
902
  }
884
- const target = join6(repoRoot, job.captureFile);
903
+ const target = join7(repoRoot, job.captureFile);
885
904
  const temp = `${target}.hermes-tmp`;
886
905
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
887
906
  writeFileSync3(temp, renderCaptureMarkdown(upgraded, date), "utf8");
@@ -1127,7 +1146,7 @@ function needsLlm(session) {
1127
1146
 
1128
1147
  // src/consolidate/scheduleConsolidate.ts
1129
1148
  import { spawn as spawn2 } from "child_process";
1130
- import { dirname as dirname5, join as join11 } from "path";
1149
+ import { dirname as dirname4, join as join12 } from "path";
1131
1150
  import { fileURLToPath as fileURLToPath2 } from "url";
1132
1151
 
1133
1152
  // src/consolidate/state.ts
@@ -1222,7 +1241,7 @@ function isLockStale(lock, ttlMs) {
1222
1241
 
1223
1242
  // src/consolidate/sessionScanner.ts
1224
1243
  import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
1225
- import { join as join7 } from "path";
1244
+ import { join as join8 } from "path";
1226
1245
  function parseSessionFrontmatter(content) {
1227
1246
  const match = content.match(/^---\n([\s\S]*?)\n---/);
1228
1247
  if (!match) return null;
@@ -1250,7 +1269,7 @@ function scanAllSessions(repoRoot) {
1250
1269
  }
1251
1270
  const sessions = [];
1252
1271
  for (const file of files) {
1253
- const absolutePath = join7(rawDir, file);
1272
+ const absolutePath = join8(rawDir, file);
1254
1273
  try {
1255
1274
  const content = readFileSync8(absolutePath, "utf8");
1256
1275
  const fm = parseSessionFrontmatter(content);
@@ -1262,7 +1281,7 @@ function scanAllSessions(repoRoot) {
1262
1281
  sessionId: fm.sessionId,
1263
1282
  filename: file,
1264
1283
  absolutePath,
1265
- relativePath: join7(".memory", "captures", "raw", file),
1284
+ relativePath: join8(".memory", "captures", "raw", file),
1266
1285
  frontmatter: fm,
1267
1286
  bodyContent: bodyContent.trim()
1268
1287
  });
@@ -1282,7 +1301,7 @@ function filterPendingSessions(sessions) {
1282
1301
 
1283
1302
  // src/consolidate/llmConsolidateV2.ts
1284
1303
  import { readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
1285
- import { join as join8 } from "path";
1304
+ import { join as join9 } from "path";
1286
1305
  var CONSOLIDATE_SYSTEM_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u9879\u76EE\u77E5\u8BC6\u6574\u7406\u4E13\u5BB6\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u4ECE AI \u7F16\u7A0B\u52A9\u624B\u7684\u5BF9\u8BDD\u8BB0\u5F55\u4E2D\u63D0\u70BC\u51FA\u7ED3\u6784\u5316\u7684\u77E5\u8BC6\u5E93\u3002
1287
1306
 
1288
1307
  ## \u5DE5\u4F5C\u6D41\u7A0B
@@ -1349,7 +1368,11 @@ var CONSOLIDATE_SYSTEM_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u9879\u76EE\u77E5\u8BC
1349
1368
  ## \u8F93\u51FA\u8981\u6C42
1350
1369
  - \u8F93\u51FA\u4E25\u683C JSON \u683C\u5F0F
1351
1370
  - knowledgeFiles \u6570\u7EC4\u5305\u542B\u6240\u6709\u9700\u8981\u521B\u5EFA/\u66F4\u65B0\u7684\u6587\u4EF6
1352
- - \u6BCF\u9879\u5FC5\u987B\u5305\u542B\u5B8C\u6574\u7684 frontmatter \u548C body markdown
1371
+ - \u6BCF\u9879\u5FC5\u987B\u5305\u542B targetPath\u3001action\u3001frontmatter\u3001body
1372
+ - targetPath \u662F\u76F8\u5BF9 .memory/ \u7684\u8DEF\u5F84\uFF0C\u4F8B\u5982 "domains/canvas/canvas-interaction.md"
1373
+ - \u4E0D\u8981\u4F7F\u7528 path\u3001filePath\u3001filename \u7B49\u5B57\u6BB5\u4EE3\u66FF targetPath
1374
+ - frontmatter \u5FC5\u987B\u662F JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA YAML \u5B57\u7B26\u4E32
1375
+ - body \u662F\u4E0D\u5305\u542B frontmatter \u7684 markdown \u6B63\u6587
1353
1376
  - memoryMd \u662F\u5B8C\u6574 MEMORY.md \u5185\u5BB9
1354
1377
  - \u65E0\u4EF7\u503C\u7684 session \u5728 skippedSessions \u4E2D\u8BF4\u660E\u539F\u56E0\uFF08\u800C\u975E\u751F\u6210\u7A7A\u5185\u5BB9\uFF09`;
1355
1378
  function scanExistingKnowledge(repoRoot) {
@@ -1368,7 +1391,7 @@ function scanExistingKnowledge(repoRoot) {
1368
1391
  try {
1369
1392
  const subdirs = readdirSync3(domainsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1370
1393
  for (const domain of subdirs) {
1371
- const domainAbsDir = join8(domainsDir, domain);
1394
+ const domainAbsDir = join9(domainsDir, domain);
1372
1395
  scanMarkdownDirectory(
1373
1396
  domainAbsDir,
1374
1397
  `domains/${domain}`,
@@ -1390,7 +1413,7 @@ function scanMarkdownDirectory(absoluteDir, relativePrefix, type, domain, result
1390
1413
  }
1391
1414
  for (const file of files) {
1392
1415
  try {
1393
- const content = readFileSync9(join8(absoluteDir, file), "utf8");
1416
+ const content = readFileSync9(join9(absoluteDir, file), "utf8");
1394
1417
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
1395
1418
  let title = file.replace(/\.md$/, "");
1396
1419
  if (fmMatch) {
@@ -1568,16 +1591,51 @@ function validateAndNormalizeLlmResult(raw) {
1568
1591
  function normalizeKnowledgeFile(raw) {
1569
1592
  if (!raw || typeof raw !== "object") return null;
1570
1593
  const obj = raw;
1571
- if (typeof obj.targetPath !== "string" || !["create", "update"].includes(obj.action) || typeof obj.body !== "string") {
1594
+ const targetPath = typeof obj.targetPath === "string" ? obj.targetPath : typeof obj.path === "string" ? obj.path : null;
1595
+ if (!targetPath || !["create", "update"].includes(obj.action) || typeof obj.body !== "string") {
1572
1596
  return null;
1573
1597
  }
1574
1598
  return {
1575
- targetPath: obj.targetPath,
1599
+ targetPath,
1576
1600
  action: obj.action,
1577
- frontmatter: typeof obj.frontmatter === "object" && obj.frontmatter !== null ? obj.frontmatter : {},
1601
+ frontmatter: normalizeFrontmatter(obj.frontmatter),
1578
1602
  body: obj.body
1579
1603
  };
1580
1604
  }
1605
+ function normalizeFrontmatter(raw) {
1606
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
1607
+ return raw;
1608
+ }
1609
+ if (typeof raw !== "string") {
1610
+ return {};
1611
+ }
1612
+ const text = raw.trim();
1613
+ const yaml = text.startsWith("---") ? text.replace(/^---\r?\n?/, "").replace(/\r?\n?---$/, "") : text;
1614
+ const frontmatter = {};
1615
+ for (const line of yaml.split(/\r?\n/)) {
1616
+ const trimmed = line.trim();
1617
+ if (!trimmed || trimmed.startsWith("#")) {
1618
+ continue;
1619
+ }
1620
+ const separator = trimmed.indexOf(":");
1621
+ if (separator <= 0) {
1622
+ continue;
1623
+ }
1624
+ const key = trimmed.slice(0, separator).trim();
1625
+ const value = trimmed.slice(separator + 1).trim();
1626
+ frontmatter[key] = parseFrontmatterValue(value);
1627
+ }
1628
+ return frontmatter;
1629
+ }
1630
+ function parseFrontmatterValue(value) {
1631
+ const unquoted = value.replace(/^["']|["']$/g, "");
1632
+ if (unquoted.startsWith("[") && unquoted.endsWith("]")) {
1633
+ return unquoted.slice(1, -1).split(",").map((item) => item.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
1634
+ }
1635
+ if (unquoted === "true") return true;
1636
+ if (unquoted === "false") return false;
1637
+ return unquoted;
1638
+ }
1581
1639
  function isSkippedEntry(raw) {
1582
1640
  if (!raw || typeof raw !== "object") return false;
1583
1641
  const obj = raw;
@@ -1586,7 +1644,7 @@ function isSkippedEntry(raw) {
1586
1644
 
1587
1645
  // src/consolidate/writeKnowledge.ts
1588
1646
  import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
1589
- import { dirname as dirname4 } from "path";
1647
+ import { dirname as dirname3 } from "path";
1590
1648
  var KNOWLEDGE_LINK_PREFIXES = [
1591
1649
  "rules/",
1592
1650
  "domains/",
@@ -1603,7 +1661,7 @@ function writeKnowledgeFiles(repoRoot, files) {
1603
1661
  for (const kf of files) {
1604
1662
  try {
1605
1663
  const absolutePath = memoryPath(repoRoot, kf.targetPath);
1606
- const dir = dirname4(absolutePath);
1664
+ const dir = dirname3(absolutePath);
1607
1665
  mkdirSync5(dir, { recursive: true });
1608
1666
  const content = serializeKnowledgeFile(kf.frontmatter, kf.body);
1609
1667
  const alreadyExists = existsSync7(absolutePath);
@@ -1731,7 +1789,7 @@ ${endMarker}` + result.slice(endIdx + endMarker.length);
1731
1789
 
1732
1790
  // src/consolidate/archive.ts
1733
1791
  import { mkdirSync as mkdirSync6, renameSync as renameSync2 } from "fs";
1734
- import { join as join10 } from "path";
1792
+ import { join as join11 } from "path";
1735
1793
  function archiveDoneSessions(repoRoot, sessions, autoArchiveDays = 30) {
1736
1794
  const archivedDir = memoryPath(repoRoot, "captures", "archived");
1737
1795
  const cutoffMs = Date.now() - autoArchiveDays * 24 * 60 * 60 * 1e3;
@@ -1744,7 +1802,7 @@ function archiveDoneSessions(repoRoot, sessions, autoArchiveDays = 30) {
1744
1802
  if (consolidatedTime > cutoffMs) continue;
1745
1803
  try {
1746
1804
  mkdirSync6(archivedDir, { recursive: true });
1747
- renameSync2(s.absolutePath, join10(archivedDir, s.filename));
1805
+ renameSync2(s.absolutePath, join11(archivedDir, s.filename));
1748
1806
  archivedCount++;
1749
1807
  } catch {
1750
1808
  }
@@ -1926,7 +1984,7 @@ var CONSOLIDATE_LOCK_TTL_MS = 30 * 60 * 1e3;
1926
1984
 
1927
1985
  // src/consolidate/scheduleConsolidate.ts
1928
1986
  function cliPath2() {
1929
- return join11(dirname5(fileURLToPath2(import.meta.url)), "..", "cli.js");
1987
+ return join12(dirname4(fileURLToPath2(import.meta.url)), "..", "cli.js");
1930
1988
  }
1931
1989
  async function runFlushCommand(opts) {
1932
1990
  const ctx = loadRepoContext(opts.cwd);
@@ -2111,7 +2169,7 @@ async function commitCapture(opts) {
2111
2169
  // src/capture/claude-code/resolveSession.ts
2112
2170
  import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync } from "fs";
2113
2171
  import { homedir } from "os";
2114
- import { basename as basename2, join as join12, resolve as resolve3 } from "path";
2172
+ import { basename as basename2, join as join13, resolve as resolve3 } from "path";
2115
2173
  function encodeClaudeProjectDir(absPath) {
2116
2174
  return resolve3(absPath).replace(/\//g, "-");
2117
2175
  }
@@ -2125,20 +2183,20 @@ function resolveSessionJsonlPath(repoRoot, options = {}) {
2125
2183
  return resolve3(fromHook);
2126
2184
  }
2127
2185
  const sessionId = process.env.CLAUDE_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? process.env.SESSION_ID;
2128
- const claudeHome = process.env.CLAUDE_CONFIG_DIR ? resolve3(process.env.CLAUDE_CONFIG_DIR) : join12(homedir(), ".claude");
2129
- const projectsRoot = join12(claudeHome, "projects");
2186
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR ? resolve3(process.env.CLAUDE_CONFIG_DIR) : join13(homedir(), ".claude");
2187
+ const projectsRoot = join13(claudeHome, "projects");
2130
2188
  if (!existsSync9(projectsRoot)) {
2131
2189
  return null;
2132
2190
  }
2133
2191
  const cwd = resolve3(options.cwd ?? repoRoot);
2134
2192
  const preferredProjectDir = encodeClaudeProjectDir(cwd);
2135
- const preferredPath = join12(projectsRoot, preferredProjectDir);
2193
+ const preferredPath = join13(projectsRoot, preferredProjectDir);
2136
2194
  if (existsSync9(preferredPath)) {
2137
2195
  const hit = pickNewestJsonl(preferredPath, sessionId);
2138
2196
  if (hit) {
2139
2197
  return hit;
2140
2198
  }
2141
- const legacySessions = join12(preferredPath, "sessions");
2199
+ const legacySessions = join13(preferredPath, "sessions");
2142
2200
  if (existsSync9(legacySessions)) {
2143
2201
  const legacyHit = pickNewestJsonl(legacySessions, sessionId);
2144
2202
  if (legacyHit) {
@@ -2149,9 +2207,9 @@ function resolveSessionJsonlPath(repoRoot, options = {}) {
2149
2207
  const candidates = [];
2150
2208
  for (const projectDir of readdirSync4(projectsRoot, { withFileTypes: true })) {
2151
2209
  if (!projectDir.isDirectory()) continue;
2152
- const projectPath = join12(projectsRoot, projectDir.name);
2210
+ const projectPath = join13(projectsRoot, projectDir.name);
2153
2211
  collectJsonlCandidates(projectPath, sessionId, candidates);
2154
- const legacySessions = join12(projectPath, "sessions");
2212
+ const legacySessions = join13(projectPath, "sessions");
2155
2213
  if (existsSync9(legacySessions)) {
2156
2214
  collectJsonlCandidates(legacySessions, sessionId, candidates);
2157
2215
  }
@@ -2179,7 +2237,7 @@ function collectJsonlCandidates(dir, sessionId, out) {
2179
2237
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
2180
2238
  continue;
2181
2239
  }
2182
- const fullPath = join12(dir, entry.name);
2240
+ const fullPath = join13(dir, entry.name);
2183
2241
  if (sessionId && !entry.name.includes(sessionId) && basename2(entry.name, ".jsonl") !== sessionId) {
2184
2242
  continue;
2185
2243
  }
@@ -2214,7 +2272,7 @@ async function runClaudeCodeCapture(repoRoot, cwd, dryRun, options) {
2214
2272
  // src/capture/codebuddy/resolveSession.ts
2215
2273
  import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
2216
2274
  import { homedir as homedir2 } from "os";
2217
- import { basename as basename3, join as join13, resolve as resolve4 } from "path";
2275
+ import { basename as basename3, join as join14, resolve as resolve4 } from "path";
2218
2276
  function encodeCodebuddyProjectDir(absPath) {
2219
2277
  return resolve4(absPath).replace(/^\//, "").replace(/\//g, "-");
2220
2278
  }
@@ -2228,14 +2286,14 @@ function resolveCodebuddySessionJsonl(options) {
2228
2286
  return resolve4(fromHook);
2229
2287
  }
2230
2288
  const sessionId = process.env.CODEBUDDY_SESSION_ID ?? process.env.SESSION_ID;
2231
- const codebuddyHome = process.env.CODEBUDDY_CONFIG_DIR ? resolve4(process.env.CODEBUDDY_CONFIG_DIR) : join13(homedir2(), ".codebuddy");
2232
- const projectsRoot = join13(codebuddyHome, "projects");
2289
+ const codebuddyHome = process.env.CODEBUDDY_CONFIG_DIR ? resolve4(process.env.CODEBUDDY_CONFIG_DIR) : join14(homedir2(), ".codebuddy");
2290
+ const projectsRoot = join14(codebuddyHome, "projects");
2233
2291
  if (!existsSync10(projectsRoot)) {
2234
2292
  return null;
2235
2293
  }
2236
2294
  const cwd = resolve4(options.cwd ?? options.repoRoot);
2237
2295
  const preferredProjectDir = encodeCodebuddyProjectDir(cwd);
2238
- const preferredPath = join13(projectsRoot, preferredProjectDir);
2296
+ const preferredPath = join14(projectsRoot, preferredProjectDir);
2239
2297
  if (existsSync10(preferredPath)) {
2240
2298
  const hit = pickNewestJsonlRecursive(preferredPath, sessionId);
2241
2299
  if (hit) {
@@ -2247,7 +2305,7 @@ function resolveCodebuddySessionJsonl(options) {
2247
2305
  if (!projectDir.isDirectory()) {
2248
2306
  continue;
2249
2307
  }
2250
- collectJsonlRecursive(join13(projectsRoot, projectDir.name), sessionId, candidates);
2308
+ collectJsonlRecursive(join14(projectsRoot, projectDir.name), sessionId, candidates);
2251
2309
  }
2252
2310
  if (candidates.length === 0) {
2253
2311
  return null;
@@ -2269,7 +2327,7 @@ function collectJsonlRecursive(dir, sessionId, out) {
2269
2327
  return;
2270
2328
  }
2271
2329
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
2272
- const full = join13(dir, entry.name);
2330
+ const full = join14(dir, entry.name);
2273
2331
  if (entry.isDirectory()) {
2274
2332
  collectJsonlRecursive(full, sessionId, out);
2275
2333
  continue;
@@ -2319,7 +2377,7 @@ async function runCodebuddyCapture(repoRoot, cwd, dryRun, options) {
2319
2377
  // src/capture/cursor/resolveSession.ts
2320
2378
  import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
2321
2379
  import { homedir as homedir3 } from "os";
2322
- import { join as join14, resolve as resolve5 } from "path";
2380
+ import { join as join15, resolve as resolve5 } from "path";
2323
2381
  function encodeCursorProjectDir(absPath) {
2324
2382
  return resolve5(absPath).replace(/^\//, "").replace(/\//g, "-");
2325
2383
  }
@@ -2328,25 +2386,25 @@ function resolveCursorSessionJsonl(options) {
2328
2386
  if (override && existsSync11(override)) {
2329
2387
  return resolve5(override);
2330
2388
  }
2331
- const cursorHome = process.env.CURSOR_CONFIG_DIR ? resolve5(process.env.CURSOR_CONFIG_DIR) : join14(homedir3(), ".cursor");
2332
- const projectsRoot = join14(cursorHome, "projects");
2389
+ const cursorHome = process.env.CURSOR_CONFIG_DIR ? resolve5(process.env.CURSOR_CONFIG_DIR) : join15(homedir3(), ".cursor");
2390
+ const projectsRoot = join15(cursorHome, "projects");
2333
2391
  if (!existsSync11(projectsRoot)) {
2334
2392
  return null;
2335
2393
  }
2336
2394
  const sessionId = options.hookInput?.sessionId ?? options.hookInput?.conversationId ?? process.env.CURSOR_SESSION_ID ?? process.env.CURSOR_AGENT_SESSION_ID;
2337
2395
  const workspace = options.hookInput?.workspaceRoots?.[0] ?? (options.cwd ? resolve5(options.cwd) : resolve5(options.repoRoot));
2338
2396
  const encoded = encodeCursorProjectDir(workspace);
2339
- const projectDir = join14(projectsRoot, encoded);
2340
- const transcriptsRoot = join14(projectDir, "agent-transcripts");
2397
+ const projectDir = join15(projectsRoot, encoded);
2398
+ const transcriptsRoot = join15(projectDir, "agent-transcripts");
2341
2399
  if (!existsSync11(transcriptsRoot)) {
2342
2400
  return pickNewestCursorJsonl(projectsRoot);
2343
2401
  }
2344
2402
  if (sessionId) {
2345
- const direct = join14(transcriptsRoot, sessionId, `${sessionId}.jsonl`);
2403
+ const direct = join15(transcriptsRoot, sessionId, `${sessionId}.jsonl`);
2346
2404
  if (existsSync11(direct)) {
2347
2405
  return direct;
2348
2406
  }
2349
- const nested = findJsonlUnderDir(join14(transcriptsRoot, sessionId), sessionId);
2407
+ const nested = findJsonlUnderDir(join15(transcriptsRoot, sessionId), sessionId);
2350
2408
  if (nested) {
2351
2409
  return nested;
2352
2410
  }
@@ -2357,16 +2415,16 @@ function findJsonlUnderDir(dir, sessionId) {
2357
2415
  if (!existsSync11(dir)) {
2358
2416
  return null;
2359
2417
  }
2360
- const direct = join14(dir, `${sessionId}.jsonl`);
2418
+ const direct = join15(dir, `${sessionId}.jsonl`);
2361
2419
  if (existsSync11(direct)) {
2362
2420
  return direct;
2363
2421
  }
2364
2422
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
2365
2423
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
2366
- return join14(dir, entry.name);
2424
+ return join15(dir, entry.name);
2367
2425
  }
2368
2426
  if (entry.isDirectory()) {
2369
- const found = findJsonlUnderDir(join14(dir, entry.name), sessionId);
2427
+ const found = findJsonlUnderDir(join15(dir, entry.name), sessionId);
2370
2428
  if (found) {
2371
2429
  return found;
2372
2430
  }
@@ -2388,7 +2446,7 @@ function collectJsonlRecursive2(dir, out) {
2388
2446
  return;
2389
2447
  }
2390
2448
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
2391
- const full = join14(dir, entry.name);
2449
+ const full = join15(dir, entry.name);
2392
2450
  if (entry.isDirectory()) {
2393
2451
  collectJsonlRecursive2(full, out);
2394
2452
  continue;
@@ -2632,7 +2690,7 @@ async function runFlushCommandCli(opts) {
2632
2690
 
2633
2691
  // src/inject/runInject.ts
2634
2692
  import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync13 } from "fs";
2635
- import { join as join15 } from "path";
2693
+ import { join as join16 } from "path";
2636
2694
 
2637
2695
  // src/inject/constants.ts
2638
2696
  var INJECT_MAX_CHARS = 8e3;
@@ -2712,7 +2770,7 @@ function readAllRules(repoRoot) {
2712
2770
  const parts = [];
2713
2771
  for (const file of files) {
2714
2772
  try {
2715
- const filePath = join15(rulesDir, file);
2773
+ const filePath = join16(rulesDir, file);
2716
2774
  const content = readFileSync13(filePath, "utf8").trim();
2717
2775
  if (!content) continue;
2718
2776
  parts.push(`### ${file}`, "", content, "");
@@ -2740,46 +2798,46 @@ import { resolve as resolve7 } from "path";
2740
2798
 
2741
2799
  // src/init/assistants/claude-code.ts
2742
2800
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
2743
- import { join as join19 } from "path";
2801
+ import { join as join20 } from "path";
2744
2802
 
2745
2803
  // src/init/mergeClaudeSettings.ts
2746
2804
  import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
2747
- import { join as join18 } from "path";
2805
+ import { join as join19 } from "path";
2748
2806
 
2749
2807
  // src/init/templateDir.ts
2750
2808
  import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
2751
- import { dirname as dirname7, join as join17 } from "path";
2809
+ import { dirname as dirname6, join as join18 } from "path";
2752
2810
  import { fileURLToPath as fileURLToPath4 } from "url";
2753
2811
 
2754
2812
  // src/index.ts
2755
2813
  import { readFileSync as readFileSync14 } from "fs";
2756
- import { dirname as dirname6, join as join16 } from "path";
2814
+ import { dirname as dirname5, join as join17 } from "path";
2757
2815
  import { fileURLToPath as fileURLToPath3 } from "url";
2758
2816
  var PACKAGE_NAME = "@riconext/hermes-repo";
2759
- var __dirname = dirname6(fileURLToPath3(import.meta.url));
2817
+ var __dirname = dirname5(fileURLToPath3(import.meta.url));
2760
2818
  function readPkgVersion() {
2761
- const pkgPath = join16(__dirname, "..", "package.json");
2819
+ const pkgPath = join17(__dirname, "..", "package.json");
2762
2820
  const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
2763
2821
  return pkg.version;
2764
2822
  }
2765
2823
 
2766
2824
  // src/init/templateDir.ts
2767
2825
  function resolveTemplateDir() {
2768
- const here = dirname7(fileURLToPath4(import.meta.url));
2826
+ const here = dirname6(fileURLToPath4(import.meta.url));
2769
2827
  const candidates = [
2770
- join17(here, "templates"),
2771
- join17(here, "..", "..", "templates")
2828
+ join18(here, "templates"),
2829
+ join18(here, "..", "..", "templates")
2772
2830
  ];
2773
2831
  for (const dir of candidates) {
2774
2832
  if (existsSync13(dir)) {
2775
2833
  return dir;
2776
2834
  }
2777
2835
  }
2778
- return join17(here, "templates");
2836
+ return join18(here, "templates");
2779
2837
  }
2780
2838
  var templateDir = resolveTemplateDir();
2781
2839
  function resolveTemplatePath(name) {
2782
- return join17(templateDir, name);
2840
+ return join18(templateDir, name);
2783
2841
  }
2784
2842
  function readTemplate(name) {
2785
2843
  return readFileSync15(resolveTemplatePath(name), "utf8");
@@ -2792,7 +2850,7 @@ function renderTemplate(name) {
2792
2850
  // src/init/mergeClaudeSettings.ts
2793
2851
  var CLAUDE_SETTINGS_LOCAL_REL = ".claude/settings.local.json";
2794
2852
  function claudeSettingsLocalPath(repoRoot) {
2795
- return join18(repoRoot, ".claude", "settings.local.json");
2853
+ return join19(repoRoot, ".claude", "settings.local.json");
2796
2854
  }
2797
2855
  function mergeClaudeLocalSettings(repoRoot) {
2798
2856
  const settingsPath = claudeSettingsLocalPath(repoRoot);
@@ -2829,7 +2887,7 @@ var claudeCodeAdapter = {
2829
2887
  available: true,
2830
2888
  scaffoldPaths: [CLAUDE_SETTINGS_LOCAL_REL],
2831
2889
  write(ctx) {
2832
- mkdirSync7(join19(ctx.repoRoot, ".claude"), { recursive: true });
2890
+ mkdirSync7(join20(ctx.repoRoot, ".claude"), { recursive: true });
2833
2891
  const { content, action } = mergeClaudeLocalSettings(ctx.repoRoot);
2834
2892
  writeFileSync6(claudeSettingsLocalPath(ctx.repoRoot), content, "utf8");
2835
2893
  ctx.report.files.push({ path: CLAUDE_SETTINGS_LOCAL_REL, action });
@@ -2838,11 +2896,11 @@ var claudeCodeAdapter = {
2838
2896
 
2839
2897
  // src/init/assistants/codex.ts
2840
2898
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "fs";
2841
- import { join as join21 } from "path";
2899
+ import { join as join22 } from "path";
2842
2900
 
2843
2901
  // src/init/mergeCodexConfig.ts
2844
2902
  import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
2845
- import { join as join20 } from "path";
2903
+ import { join as join21 } from "path";
2846
2904
  var CODEX_CONFIG_REL = ".codex/config.toml";
2847
2905
  var CODEX_HERMES_START_MARKER = "# >>> hermes-repo codex (do not edit this block manually)";
2848
2906
  var CODEX_HERMES_END_MARKER = "# <<< hermes-repo codex";
@@ -2856,7 +2914,7 @@ function buildCodexHermesBlock() {
2856
2914
  ].join("\n");
2857
2915
  }
2858
2916
  function codexConfigPath(repoRoot) {
2859
- return join20(repoRoot, ".codex", "config.toml");
2917
+ return join21(repoRoot, ".codex", "config.toml");
2860
2918
  }
2861
2919
  function spliceHermesBlock(existing, block) {
2862
2920
  const startIdx = existing.indexOf(CODEX_HERMES_START_MARKER);
@@ -2903,7 +2961,7 @@ var codexAdapter = {
2903
2961
  available: true,
2904
2962
  scaffoldPaths: [CODEX_CONFIG_REL],
2905
2963
  write(ctx) {
2906
- mkdirSync8(join21(ctx.repoRoot, ".codex"), { recursive: true });
2964
+ mkdirSync8(join22(ctx.repoRoot, ".codex"), { recursive: true });
2907
2965
  const { content, action } = mergeCodexConfig(ctx.repoRoot);
2908
2966
  writeFileSync7(codexConfigPath(ctx.repoRoot), content, "utf8");
2909
2967
  ctx.report.files.push({ path: CODEX_CONFIG_REL, action });
@@ -2912,14 +2970,14 @@ var codexAdapter = {
2912
2970
 
2913
2971
  // src/init/assistants/codebuddy.ts
2914
2972
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
2915
- import { join as join23 } from "path";
2973
+ import { join as join24 } from "path";
2916
2974
 
2917
2975
  // src/init/mergeCodebuddySettings.ts
2918
2976
  import { existsSync as existsSync16, readFileSync as readFileSync18 } from "fs";
2919
- import { join as join22 } from "path";
2977
+ import { join as join23 } from "path";
2920
2978
  var CODEBUDDY_SETTINGS_LOCAL_REL = ".codebuddy/settings.local.json";
2921
2979
  function codebuddySettingsLocalPath(repoRoot) {
2922
- return join22(repoRoot, ".codebuddy", "settings.local.json");
2980
+ return join23(repoRoot, ".codebuddy", "settings.local.json");
2923
2981
  }
2924
2982
  function mergeCodebuddyLocalSettings(repoRoot) {
2925
2983
  const settingsPath = codebuddySettingsLocalPath(repoRoot);
@@ -2958,7 +3016,7 @@ var codebuddyAdapter = {
2958
3016
  available: true,
2959
3017
  scaffoldPaths: [CODEBUDDY_SETTINGS_LOCAL_REL],
2960
3018
  write(ctx) {
2961
- mkdirSync9(join23(ctx.repoRoot, ".codebuddy"), { recursive: true });
3019
+ mkdirSync9(join24(ctx.repoRoot, ".codebuddy"), { recursive: true });
2962
3020
  const { content, action } = mergeCodebuddyLocalSettings(ctx.repoRoot);
2963
3021
  writeFileSync8(codebuddySettingsLocalPath(ctx.repoRoot), content, "utf8");
2964
3022
  ctx.report.files.push({ path: CODEBUDDY_SETTINGS_LOCAL_REL, action });
@@ -2967,14 +3025,14 @@ var codebuddyAdapter = {
2967
3025
 
2968
3026
  // src/init/assistants/cursor.ts
2969
3027
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
2970
- import { join as join25 } from "path";
3028
+ import { join as join26 } from "path";
2971
3029
 
2972
3030
  // src/init/mergeCursorHooks.ts
2973
3031
  import { existsSync as existsSync17, readFileSync as readFileSync19 } from "fs";
2974
- import { join as join24 } from "path";
3032
+ import { join as join25 } from "path";
2975
3033
  var CURSOR_HOOKS_REL = ".cursor/hooks.json";
2976
3034
  function cursorHooksPath(repoRoot) {
2977
- return join24(repoRoot, ".cursor", "hooks.json");
3035
+ return join25(repoRoot, ".cursor", "hooks.json");
2978
3036
  }
2979
3037
  function mergeCursorHooks(repoRoot) {
2980
3038
  const hooksPath = cursorHooksPath(repoRoot);
@@ -3012,7 +3070,7 @@ var cursorAdapter = {
3012
3070
  available: true,
3013
3071
  scaffoldPaths: [CURSOR_HOOKS_REL],
3014
3072
  write(ctx) {
3015
- mkdirSync10(join25(ctx.repoRoot, ".cursor"), { recursive: true });
3073
+ mkdirSync10(join26(ctx.repoRoot, ".cursor"), { recursive: true });
3016
3074
  const { content, action } = mergeCursorHooks(ctx.repoRoot);
3017
3075
  writeFileSync9(cursorHooksPath(ctx.repoRoot), content, "utf8");
3018
3076
  ctx.report.files.push({ path: CURSOR_HOOKS_REL, action });
@@ -3075,16 +3133,16 @@ function validateAssistantSelection(ids) {
3075
3133
 
3076
3134
  // src/init/ensureDirs.ts
3077
3135
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
3078
- import { join as join26 } from "path";
3136
+ import { join as join27 } from "path";
3079
3137
  function ensureMemoryTree(repoRoot) {
3080
- const memoryRoot = join26(repoRoot, MEMORY_DIR);
3138
+ const memoryRoot = join27(repoRoot, MEMORY_DIR);
3081
3139
  mkdirSync11(memoryRoot, { recursive: true });
3082
3140
  for (const sub of MEMORY_SUBDIRS) {
3083
- mkdirSync11(join26(memoryRoot, sub), { recursive: true });
3141
+ mkdirSync11(join27(memoryRoot, sub), { recursive: true });
3084
3142
  }
3085
- mkdirSync11(join26(repoRoot, ".claude"), { recursive: true });
3143
+ mkdirSync11(join27(repoRoot, ".claude"), { recursive: true });
3086
3144
  for (const sub of GITKEEP_DIRS) {
3087
- const keepPath = join26(memoryRoot, sub, ".gitkeep");
3145
+ const keepPath = join27(memoryRoot, sub, ".gitkeep");
3088
3146
  writeFileSync10(keepPath, "", { flag: "a" });
3089
3147
  }
3090
3148
  }
@@ -3113,12 +3171,12 @@ function mergeAssistants(repoRoot, selected) {
3113
3171
 
3114
3172
  // src/init/mergeGitignore.ts
3115
3173
  import { existsSync as existsSync19, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "fs";
3116
- import { join as join27 } from "path";
3174
+ import { join as join28 } from "path";
3117
3175
  var START_MARKER = "# >>> hermes-repo memory (do not edit this block manually)";
3118
3176
  var END_MARKER = "# <<< hermes-repo memory";
3119
3177
  function mergeHermesGitignore(repoRoot) {
3120
3178
  const block = readTemplate("gitignore-block.txt").trimEnd() + "\n";
3121
- const gitignorePath = join27(repoRoot, ".gitignore");
3179
+ const gitignorePath = join28(repoRoot, ".gitignore");
3122
3180
  const contentBefore = existsSync19(gitignorePath) ? readFileSync21(gitignorePath, "utf8") : "";
3123
3181
  const warnBroadMemoryIgnore = contentBefore.length > 0 && !contentBefore.includes(START_MARKER) && /(^|\n)\.memory\/\s*$/m.test(contentBefore);
3124
3182
  if (!existsSync19(gitignorePath)) {
@@ -3216,7 +3274,7 @@ function mergeConfigForInit(repoRoot, assistants) {
3216
3274
 
3217
3275
  // src/init/mergeAgentsMd.ts
3218
3276
  import { existsSync as existsSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
3219
- import { join as join28 } from "path";
3277
+ import { join as join29 } from "path";
3220
3278
  var HERMES_AGENTS_START_MARKER = "<!-- >>> hermes-repo agents (do not edit this block manually) -->";
3221
3279
  var HERMES_AGENTS_END_MARKER = "<!-- <<< hermes-repo agents -->";
3222
3280
  function buildHermesAgentsBlockBody() {
@@ -3272,7 +3330,7 @@ function spliceHermesBlock2(existing, block) {
3272
3330
  `;
3273
3331
  }
3274
3332
  function mergeAgentsMd(repoRoot, force) {
3275
- const agentsPath = join28(repoRoot, "AGENTS.md");
3333
+ const agentsPath = join29(repoRoot, "AGENTS.md");
3276
3334
  const block = buildHermesAgentsMarkedBlock();
3277
3335
  if (!existsSync21(agentsPath)) {
3278
3336
  writeFileSync12(agentsPath, buildNewAgentsMd(), "utf8");