@riconext/hermes-repo 1.0.0 → 1.1.1
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 +12 -0
- package/README.md +17 -18
- package/dist/cli.js +140 -153
- package/dist/cli.js.map +1 -1
- package/dist/templates/AGENTS.md.tpl +2 -6
- package/dist/templates/config.json.tpl +1 -1
- package/dist/templates/gitignore-block.txt +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -102,8 +102,13 @@ function parseLlmConfig(raw) {
|
|
|
102
102
|
const llm = raw.llm;
|
|
103
103
|
return {
|
|
104
104
|
enabled: typeof llm?.enabled === "boolean" ? llm.enabled : false,
|
|
105
|
+
provider: typeof llm?.provider === "string" ? llm.provider : "openai",
|
|
105
106
|
baseUrl: typeof llm?.baseUrl === "string" ? llm.baseUrl : "https://api.openai.com/v1",
|
|
106
|
-
model: typeof llm?.model === "string" ? llm.model : "gpt-4o"
|
|
107
|
+
model: typeof llm?.model === "string" ? llm.model : "gpt-4o",
|
|
108
|
+
apiKey: typeof llm?.apiKey === "string" ? llm.apiKey : "",
|
|
109
|
+
timeoutMs: typeof llm?.timeoutMs === "number" && llm.timeoutMs > 0 ? llm.timeoutMs : 6e4,
|
|
110
|
+
maxInputChars: typeof llm?.maxInputChars === "number" && llm.maxInputChars > 0 ? llm.maxInputChars : 24e3,
|
|
111
|
+
mode: llm?.mode === "sync" ? "sync" : "async"
|
|
107
112
|
};
|
|
108
113
|
}
|
|
109
114
|
function parseConsolidateConfig(raw) {
|
|
@@ -136,7 +141,16 @@ function readConfigAtRepo(repoRoot) {
|
|
|
136
141
|
storage: { backend: "file" },
|
|
137
142
|
assistants,
|
|
138
143
|
debug: raw.debug === true,
|
|
139
|
-
llm: {
|
|
144
|
+
llm: {
|
|
145
|
+
enabled: false,
|
|
146
|
+
provider: "openai",
|
|
147
|
+
baseUrl: "https://api.openai.com/v1",
|
|
148
|
+
model: "gpt-4o",
|
|
149
|
+
apiKey: "",
|
|
150
|
+
timeoutMs: 6e4,
|
|
151
|
+
maxInputChars: 24e3,
|
|
152
|
+
mode: "async"
|
|
153
|
+
},
|
|
140
154
|
consolidate: { autoArchiveDays: 30 }
|
|
141
155
|
};
|
|
142
156
|
}
|
|
@@ -158,12 +172,10 @@ function loadRepoContext(cwd) {
|
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
// src/capture/runLlmJob.ts
|
|
161
|
-
import { existsSync as
|
|
175
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
162
176
|
import { join as join6 } from "path";
|
|
163
177
|
|
|
164
178
|
// src/config/llmConfig.ts
|
|
165
|
-
var DEFAULT_LLM_TIMEOUT_MS = 6e4;
|
|
166
|
-
var DEFAULT_LLM_MAX_INPUT_CHARS = 24e3;
|
|
167
179
|
function isLlmAvailable(cfg) {
|
|
168
180
|
if (!cfg?.enabled) {
|
|
169
181
|
return false;
|
|
@@ -171,52 +183,11 @@ function isLlmAvailable(cfg) {
|
|
|
171
183
|
return Boolean(cfg.apiKey?.trim()) && Boolean(cfg.baseUrl?.trim()) && Boolean(cfg.model?.trim());
|
|
172
184
|
}
|
|
173
185
|
function effectiveLlmMode(cfg) {
|
|
174
|
-
const forceSync = process.env.HERMES_LLM_SYNC;
|
|
175
|
-
if (forceSync === "1" || forceSync === "true") {
|
|
176
|
-
return "sync";
|
|
177
|
-
}
|
|
178
186
|
return cfg.mode === "sync" ? "sync" : "async";
|
|
179
187
|
}
|
|
180
|
-
function parseLlmConfigRaw(raw) {
|
|
181
|
-
if (raw.enabled !== true && raw.enabled !== false) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
const baseUrl = typeof raw.baseUrl === "string" ? raw.baseUrl : "";
|
|
185
|
-
const model = typeof raw.model === "string" ? raw.model : "";
|
|
186
|
-
const apiKey = typeof raw.apiKey === "string" ? raw.apiKey : "";
|
|
187
|
-
const timeoutMs = typeof raw.timeoutMs === "number" && raw.timeoutMs > 0 ? raw.timeoutMs : DEFAULT_LLM_TIMEOUT_MS;
|
|
188
|
-
const maxInputChars = typeof raw.maxInputChars === "number" && raw.maxInputChars > 0 ? raw.maxInputChars : DEFAULT_LLM_MAX_INPUT_CHARS;
|
|
189
|
-
const mode = raw.mode === "sync" ? "sync" : "async";
|
|
190
|
-
const provider = typeof raw.provider === "string" ? raw.provider : "openai";
|
|
191
|
-
return {
|
|
192
|
-
enabled: raw.enabled,
|
|
193
|
-
provider,
|
|
194
|
-
baseUrl,
|
|
195
|
-
model,
|
|
196
|
-
apiKey,
|
|
197
|
-
timeoutMs,
|
|
198
|
-
maxInputChars,
|
|
199
|
-
mode
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// src/config/readLlmConfig.ts
|
|
204
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
205
|
-
function readLlmConfigAtRepo(repoRoot) {
|
|
206
|
-
const llmPath = memoryPath(repoRoot, "llm.json");
|
|
207
|
-
if (!existsSync2(llmPath)) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
const raw = JSON.parse(readFileSync2(llmPath, "utf8"));
|
|
212
|
-
return parseLlmConfigRaw(raw);
|
|
213
|
-
} catch {
|
|
214
|
-
return null;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
188
|
|
|
218
189
|
// src/capture/claude-code/parseJsonl.ts
|
|
219
|
-
import { readFileSync as
|
|
190
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
220
191
|
import { basename } from "path";
|
|
221
192
|
var FILE_CHANGE_TOOLS = /^(Write|Edit|MultiEdit|NotebookEdit|write|edit)$/i;
|
|
222
193
|
var SKIP_LINE_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -318,7 +289,7 @@ function countNestedTools(record) {
|
|
|
318
289
|
}
|
|
319
290
|
function parseJsonlFile(jsonlPath) {
|
|
320
291
|
const sessionId = basename(jsonlPath, ".jsonl");
|
|
321
|
-
const raw =
|
|
292
|
+
const raw = readFileSync2(jsonlPath, "utf8");
|
|
322
293
|
const messages = [];
|
|
323
294
|
let fileChanges = 0;
|
|
324
295
|
let toolCalls = 0;
|
|
@@ -362,10 +333,10 @@ function parseJsonlFile(jsonlPath) {
|
|
|
362
333
|
// src/capture/enqueueLlmJob.ts
|
|
363
334
|
import { spawn } from "child_process";
|
|
364
335
|
import {
|
|
365
|
-
existsSync as
|
|
336
|
+
existsSync as existsSync2,
|
|
366
337
|
mkdirSync as mkdirSync2,
|
|
367
338
|
readdirSync,
|
|
368
|
-
readFileSync as
|
|
339
|
+
readFileSync as readFileSync3,
|
|
369
340
|
rmSync,
|
|
370
341
|
writeFileSync
|
|
371
342
|
} from "fs";
|
|
@@ -383,7 +354,7 @@ function makeJobId(sessionId) {
|
|
|
383
354
|
}
|
|
384
355
|
function removeStaleJobsForSession(repoRoot, sessionId) {
|
|
385
356
|
const dir = pendingDir(repoRoot);
|
|
386
|
-
if (!
|
|
357
|
+
if (!existsSync2(dir)) {
|
|
387
358
|
return;
|
|
388
359
|
}
|
|
389
360
|
for (const name of readdirSync(dir)) {
|
|
@@ -392,7 +363,7 @@ function removeStaleJobsForSession(repoRoot, sessionId) {
|
|
|
392
363
|
}
|
|
393
364
|
try {
|
|
394
365
|
const raw = JSON.parse(
|
|
395
|
-
|
|
366
|
+
readFileSync3(join4(dir, name), "utf8")
|
|
396
367
|
);
|
|
397
368
|
if (raw.sessionId === sessionId) {
|
|
398
369
|
rmSync(join4(dir, name), { force: true });
|
|
@@ -436,24 +407,24 @@ function enqueueLlmJob(opts) {
|
|
|
436
407
|
}
|
|
437
408
|
function readLlmJob(repoRoot, jobId) {
|
|
438
409
|
const path = join4(pendingDir(repoRoot), `${jobId}.json`);
|
|
439
|
-
if (!
|
|
410
|
+
if (!existsSync2(path)) {
|
|
440
411
|
return null;
|
|
441
412
|
}
|
|
442
413
|
try {
|
|
443
|
-
return JSON.parse(
|
|
414
|
+
return JSON.parse(readFileSync3(path, "utf8"));
|
|
444
415
|
} catch {
|
|
445
416
|
return null;
|
|
446
417
|
}
|
|
447
418
|
}
|
|
448
419
|
function deleteLlmJob(repoRoot, jobId) {
|
|
449
420
|
const path = join4(pendingDir(repoRoot), `${jobId}.json`);
|
|
450
|
-
if (
|
|
421
|
+
if (existsSync2(path)) {
|
|
451
422
|
rmSync(path, { force: true });
|
|
452
423
|
}
|
|
453
424
|
}
|
|
454
425
|
function listPendingJobs(repoRoot) {
|
|
455
426
|
const dir = pendingDir(repoRoot);
|
|
456
|
-
if (!
|
|
427
|
+
if (!existsSync2(dir)) {
|
|
457
428
|
return [];
|
|
458
429
|
}
|
|
459
430
|
const jobs = [];
|
|
@@ -463,7 +434,7 @@ function listPendingJobs(repoRoot) {
|
|
|
463
434
|
}
|
|
464
435
|
try {
|
|
465
436
|
jobs.push(
|
|
466
|
-
JSON.parse(
|
|
437
|
+
JSON.parse(readFileSync3(join4(dir, name), "utf8"))
|
|
467
438
|
);
|
|
468
439
|
} catch {
|
|
469
440
|
}
|
|
@@ -693,7 +664,7 @@ async function llmFormat(session, assistant, llm) {
|
|
|
693
664
|
}
|
|
694
665
|
|
|
695
666
|
// src/capture/writeCapture.ts
|
|
696
|
-
import { existsSync as
|
|
667
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
697
668
|
import { join as join5 } from "path";
|
|
698
669
|
function isoNow() {
|
|
699
670
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -746,7 +717,7 @@ function resolveSessionFile(repoRoot, sessionId) {
|
|
|
746
717
|
const filename = `session-${sessionId}.md`;
|
|
747
718
|
const absolutePath = memoryPath(repoRoot, "captures", "raw", filename);
|
|
748
719
|
const relativePath = join5(".memory", "captures", "raw", filename);
|
|
749
|
-
return { absolutePath, relativePath, exists:
|
|
720
|
+
return { absolutePath, relativePath, exists: existsSync3(absolutePath) };
|
|
750
721
|
}
|
|
751
722
|
function renderCaptureMarkdown(formatted, date) {
|
|
752
723
|
const tagsStr = formatted.tags.map((t) => JSON.stringify(t)).join(", ");
|
|
@@ -793,7 +764,7 @@ function appendCaptureToSession(repoRoot, formatted) {
|
|
|
793
764
|
previousStatus: null
|
|
794
765
|
};
|
|
795
766
|
}
|
|
796
|
-
const existingContent =
|
|
767
|
+
const existingContent = readFileSync4(absolutePath, "utf8");
|
|
797
768
|
const existingFm = parseSessionFileFrontmatter(existingContent);
|
|
798
769
|
if (!existingFm) {
|
|
799
770
|
const fm = {
|
|
@@ -847,7 +818,7 @@ function appendCaptureToSession(repoRoot, formatted) {
|
|
|
847
818
|
function markSessionConsolidated(repoRoot, sessionId) {
|
|
848
819
|
const { absolutePath, exists } = resolveSessionFile(repoRoot, sessionId);
|
|
849
820
|
if (!exists) return;
|
|
850
|
-
const content =
|
|
821
|
+
const content = readFileSync4(absolutePath, "utf8");
|
|
851
822
|
const fm = parseSessionFileFrontmatter(content);
|
|
852
823
|
if (!fm) return;
|
|
853
824
|
const updatedFm = {
|
|
@@ -867,10 +838,10 @@ function markSessionConsolidated(repoRoot, sessionId) {
|
|
|
867
838
|
// src/capture/runLlmJob.ts
|
|
868
839
|
function captureAlreadyUpgraded(repoRoot, captureFile) {
|
|
869
840
|
const path = join6(repoRoot, captureFile);
|
|
870
|
-
if (!
|
|
841
|
+
if (!existsSync4(path)) {
|
|
871
842
|
return false;
|
|
872
843
|
}
|
|
873
|
-
const text =
|
|
844
|
+
const text = readFileSync5(path, "utf8");
|
|
874
845
|
return /llmUpgradedAt:/.test(text);
|
|
875
846
|
}
|
|
876
847
|
async function runLlmJob(repoRoot, job, debug) {
|
|
@@ -878,11 +849,11 @@ async function runLlmJob(repoRoot, job, debug) {
|
|
|
878
849
|
deleteLlmJob(repoRoot, job.jobId);
|
|
879
850
|
return { ok: true, reason: "already-upgraded" };
|
|
880
851
|
}
|
|
881
|
-
const llm =
|
|
852
|
+
const llm = readConfigAtRepo(repoRoot)?.llm ?? null;
|
|
882
853
|
if (!isLlmAvailable(llm)) {
|
|
883
854
|
return { ok: false, reason: "llm not available" };
|
|
884
855
|
}
|
|
885
|
-
if (!
|
|
856
|
+
if (!existsSync4(job.jsonlPath)) {
|
|
886
857
|
return { ok: false, reason: "jsonl missing" };
|
|
887
858
|
}
|
|
888
859
|
const session = parseJsonlFile(job.jsonlPath);
|
|
@@ -965,7 +936,7 @@ function runCaptureLlmCommand(opts) {
|
|
|
965
936
|
}
|
|
966
937
|
|
|
967
938
|
// src/capture/hookInput.ts
|
|
968
|
-
import { existsSync as
|
|
939
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
969
940
|
import { resolve as resolve2 } from "path";
|
|
970
941
|
function pickString(obj, ...keys) {
|
|
971
942
|
for (const key of keys) {
|
|
@@ -1003,7 +974,7 @@ function parseHookInputJson(raw) {
|
|
|
1003
974
|
try {
|
|
1004
975
|
const parsed = JSON.parse(trimmed);
|
|
1005
976
|
const transcriptRaw = pickString(parsed, "transcript_path", "transcriptPath");
|
|
1006
|
-
const transcriptPath = transcriptRaw &&
|
|
977
|
+
const transcriptPath = transcriptRaw && existsSync5(transcriptRaw) ? resolve2(transcriptRaw) : void 0;
|
|
1007
978
|
return {
|
|
1008
979
|
transcriptPath,
|
|
1009
980
|
transcriptPathRaw: transcriptRaw,
|
|
@@ -1022,7 +993,7 @@ function readHookInputSync() {
|
|
|
1022
993
|
return null;
|
|
1023
994
|
}
|
|
1024
995
|
try {
|
|
1025
|
-
const raw =
|
|
996
|
+
const raw = readFileSync6(0, "utf8");
|
|
1026
997
|
return parseHookInputJson(raw);
|
|
1027
998
|
} catch {
|
|
1028
999
|
return null;
|
|
@@ -1137,9 +1108,9 @@ function needsLlm(session) {
|
|
|
1137
1108
|
|
|
1138
1109
|
// src/consolidate/state.ts
|
|
1139
1110
|
import {
|
|
1140
|
-
existsSync as
|
|
1111
|
+
existsSync as existsSync6,
|
|
1141
1112
|
mkdirSync as mkdirSync4,
|
|
1142
|
-
readFileSync as
|
|
1113
|
+
readFileSync as readFileSync7,
|
|
1143
1114
|
rmSync as rmSync2,
|
|
1144
1115
|
writeFileSync as writeFileSync4
|
|
1145
1116
|
} from "fs";
|
|
@@ -1161,11 +1132,11 @@ function consolidateLockPath(repoRoot) {
|
|
|
1161
1132
|
}
|
|
1162
1133
|
function readConsolidateState(repoRoot) {
|
|
1163
1134
|
const path = consolidateStatePath(repoRoot);
|
|
1164
|
-
if (!
|
|
1135
|
+
if (!existsSync6(path)) {
|
|
1165
1136
|
return { ...EMPTY_STATE, processedSessions: {} };
|
|
1166
1137
|
}
|
|
1167
1138
|
try {
|
|
1168
|
-
const raw = JSON.parse(
|
|
1139
|
+
const raw = JSON.parse(readFileSync7(path, "utf8"));
|
|
1169
1140
|
if (typeof raw === "object" && raw !== null) {
|
|
1170
1141
|
const obj = raw;
|
|
1171
1142
|
if (obj.version === 2 && typeof obj.processedSessions === "object") {
|
|
@@ -1189,11 +1160,11 @@ function writeConsolidateState(repoRoot, state) {
|
|
|
1189
1160
|
}
|
|
1190
1161
|
function readConsolidateLock(repoRoot) {
|
|
1191
1162
|
const path = consolidateLockPath(repoRoot);
|
|
1192
|
-
if (!
|
|
1163
|
+
if (!existsSync6(path)) {
|
|
1193
1164
|
return null;
|
|
1194
1165
|
}
|
|
1195
1166
|
try {
|
|
1196
|
-
return JSON.parse(
|
|
1167
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
1197
1168
|
} catch {
|
|
1198
1169
|
return null;
|
|
1199
1170
|
}
|
|
@@ -1213,7 +1184,7 @@ function writeConsolidateLock(repoRoot) {
|
|
|
1213
1184
|
}
|
|
1214
1185
|
function releaseConsolidateLock(repoRoot) {
|
|
1215
1186
|
const path = consolidateLockPath(repoRoot);
|
|
1216
|
-
if (
|
|
1187
|
+
if (existsSync6(path)) {
|
|
1217
1188
|
rmSync2(path, { force: true });
|
|
1218
1189
|
}
|
|
1219
1190
|
}
|
|
@@ -1226,7 +1197,7 @@ function isLockStale(lock, ttlMs) {
|
|
|
1226
1197
|
}
|
|
1227
1198
|
|
|
1228
1199
|
// src/consolidate/sessionScanner.ts
|
|
1229
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
1200
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
1230
1201
|
import { join as join7 } from "path";
|
|
1231
1202
|
function parseSessionFrontmatter(content) {
|
|
1232
1203
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -1257,7 +1228,7 @@ function scanAllSessions(repoRoot) {
|
|
|
1257
1228
|
for (const file of files) {
|
|
1258
1229
|
const absolutePath = join7(rawDir, file);
|
|
1259
1230
|
try {
|
|
1260
|
-
const content =
|
|
1231
|
+
const content = readFileSync8(absolutePath, "utf8");
|
|
1261
1232
|
const fm = parseSessionFrontmatter(content);
|
|
1262
1233
|
if (!fm || !fm.sessionId) continue;
|
|
1263
1234
|
const fmEndIndex = content.indexOf("---", 4);
|
|
@@ -1286,7 +1257,7 @@ function filterPendingSessions(sessions) {
|
|
|
1286
1257
|
}
|
|
1287
1258
|
|
|
1288
1259
|
// src/consolidate/llmConsolidateV2.ts
|
|
1289
|
-
import { readFileSync as
|
|
1260
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
|
|
1290
1261
|
import { join as join8 } from "path";
|
|
1291
1262
|
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
|
|
1292
1263
|
|
|
@@ -1395,7 +1366,7 @@ function scanMarkdownDirectory(absoluteDir, relativePrefix, type, domain, result
|
|
|
1395
1366
|
}
|
|
1396
1367
|
for (const file of files) {
|
|
1397
1368
|
try {
|
|
1398
|
-
const content =
|
|
1369
|
+
const content = readFileSync9(join8(absoluteDir, file), "utf8");
|
|
1399
1370
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1400
1371
|
let title = file.replace(/\.md$/, "");
|
|
1401
1372
|
if (fmMatch) {
|
|
@@ -1421,7 +1392,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1421
1392
|
const pendingSessions = sessions.map((s) => ({
|
|
1422
1393
|
sessionId: s.sessionId,
|
|
1423
1394
|
status: s.frontmatter.status,
|
|
1424
|
-
content:
|
|
1395
|
+
content: readFileSync9(s.absolutePath, "utf8"),
|
|
1425
1396
|
captureCount: s.frontmatter.captureCount,
|
|
1426
1397
|
createdAt: s.frontmatter.createdAt
|
|
1427
1398
|
}));
|
|
@@ -1429,7 +1400,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1429
1400
|
const memoryPathAbs = memoryPath(repoRoot, "MEMORY.md");
|
|
1430
1401
|
let currentMemoryMd = null;
|
|
1431
1402
|
try {
|
|
1432
|
-
currentMemoryMd =
|
|
1403
|
+
currentMemoryMd = readFileSync9(memoryPathAbs, "utf8");
|
|
1433
1404
|
} catch {
|
|
1434
1405
|
}
|
|
1435
1406
|
return {
|
|
@@ -1439,15 +1410,14 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1439
1410
|
};
|
|
1440
1411
|
}
|
|
1441
1412
|
async function callLlmConsolidate(input2, llmConfig) {
|
|
1442
|
-
|
|
1443
|
-
if (!apiKey) {
|
|
1413
|
+
if (!llmConfig.enabled) {
|
|
1444
1414
|
throw new Error(
|
|
1445
|
-
"LLM \u672A\
|
|
1415
|
+
"LLM \u672A\u542F\u7528\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.enabled = true"
|
|
1446
1416
|
);
|
|
1447
1417
|
}
|
|
1448
|
-
if (!llmConfig.
|
|
1418
|
+
if (!llmConfig.apiKey.trim() || !llmConfig.baseUrl.trim() || !llmConfig.model.trim()) {
|
|
1449
1419
|
throw new Error(
|
|
1450
|
-
"LLM \u672A\
|
|
1420
|
+
"LLM \u672A\u914D\u7F6E\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.apiKey\u3001llm.baseUrl \u548C llm.model"
|
|
1451
1421
|
);
|
|
1452
1422
|
}
|
|
1453
1423
|
const url = `${llmConfig.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
@@ -1459,7 +1429,7 @@ async function callLlmConsolidate(input2, llmConfig) {
|
|
|
1459
1429
|
method: "POST",
|
|
1460
1430
|
headers: {
|
|
1461
1431
|
"Content-Type": "application/json",
|
|
1462
|
-
Authorization: `Bearer ${apiKey}`
|
|
1432
|
+
Authorization: `Bearer ${llmConfig.apiKey}`
|
|
1463
1433
|
},
|
|
1464
1434
|
body: JSON.stringify({
|
|
1465
1435
|
model: llmConfig.model,
|
|
@@ -1551,7 +1521,7 @@ function isSkippedEntry(raw) {
|
|
|
1551
1521
|
}
|
|
1552
1522
|
|
|
1553
1523
|
// src/consolidate/writeKnowledge.ts
|
|
1554
|
-
import { existsSync as
|
|
1524
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
1555
1525
|
import { dirname as dirname4 } from "path";
|
|
1556
1526
|
function writeKnowledgeFiles(repoRoot, files) {
|
|
1557
1527
|
const result = {
|
|
@@ -1565,7 +1535,7 @@ function writeKnowledgeFiles(repoRoot, files) {
|
|
|
1565
1535
|
const dir = dirname4(absolutePath);
|
|
1566
1536
|
mkdirSync5(dir, { recursive: true });
|
|
1567
1537
|
const content = serializeKnowledgeFile(kf.frontmatter, kf.body);
|
|
1568
|
-
const alreadyExists =
|
|
1538
|
+
const alreadyExists = existsSync7(absolutePath);
|
|
1569
1539
|
if (kf.action === "create" && alreadyExists) {
|
|
1570
1540
|
result.updated.push(kf.targetPath);
|
|
1571
1541
|
} else if (kf.action === "create") {
|
|
@@ -1585,7 +1555,7 @@ function writeKnowledgeFiles(repoRoot, files) {
|
|
|
1585
1555
|
}
|
|
1586
1556
|
function writeMemoryMd(repoRoot, memoryMd) {
|
|
1587
1557
|
const memoryPathAbs = memoryPath(repoRoot, "MEMORY.md");
|
|
1588
|
-
if (
|
|
1558
|
+
if (existsSync7(memoryPathAbs)) {
|
|
1589
1559
|
const existing = __require("fs").readFileSync(memoryPathAbs, "utf8");
|
|
1590
1560
|
const preserved = extractUserEditedSections(existing);
|
|
1591
1561
|
if (preserved.length > 0) {
|
|
@@ -1863,7 +1833,7 @@ async function commitCapture(opts) {
|
|
|
1863
1833
|
}
|
|
1864
1834
|
const result = appendCaptureToSession(repoRoot, formatted);
|
|
1865
1835
|
const captureFile = result.relativePath;
|
|
1866
|
-
const llm =
|
|
1836
|
+
const llm = readConfigAtRepo(repoRoot)?.llm ?? null;
|
|
1867
1837
|
if (!isLlmAvailable(llm) || !needsLlm(session)) {
|
|
1868
1838
|
debugLog(debug === true, "capture", `ok: ${captureFile} (format=simple)`);
|
|
1869
1839
|
return finishCapture(repoRoot, debug, {
|
|
@@ -1911,7 +1881,7 @@ async function commitCapture(opts) {
|
|
|
1911
1881
|
}
|
|
1912
1882
|
|
|
1913
1883
|
// src/capture/claude-code/resolveSession.ts
|
|
1914
|
-
import { existsSync as
|
|
1884
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync } from "fs";
|
|
1915
1885
|
import { homedir } from "os";
|
|
1916
1886
|
import { basename as basename2, join as join11, resolve as resolve3 } from "path";
|
|
1917
1887
|
function encodeClaudeProjectDir(absPath) {
|
|
@@ -1919,29 +1889,29 @@ function encodeClaudeProjectDir(absPath) {
|
|
|
1919
1889
|
}
|
|
1920
1890
|
function resolveSessionJsonlPath(repoRoot, options = {}) {
|
|
1921
1891
|
const override = process.env.HERMES_SESSION_JSONL;
|
|
1922
|
-
if (override &&
|
|
1892
|
+
if (override && existsSync9(override)) {
|
|
1923
1893
|
return resolve3(override);
|
|
1924
1894
|
}
|
|
1925
1895
|
const fromHook = options.transcriptPath;
|
|
1926
|
-
if (fromHook &&
|
|
1896
|
+
if (fromHook && existsSync9(fromHook)) {
|
|
1927
1897
|
return resolve3(fromHook);
|
|
1928
1898
|
}
|
|
1929
1899
|
const sessionId = process.env.CLAUDE_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? process.env.SESSION_ID;
|
|
1930
1900
|
const claudeHome = process.env.CLAUDE_CONFIG_DIR ? resolve3(process.env.CLAUDE_CONFIG_DIR) : join11(homedir(), ".claude");
|
|
1931
1901
|
const projectsRoot = join11(claudeHome, "projects");
|
|
1932
|
-
if (!
|
|
1902
|
+
if (!existsSync9(projectsRoot)) {
|
|
1933
1903
|
return null;
|
|
1934
1904
|
}
|
|
1935
1905
|
const cwd = resolve3(options.cwd ?? repoRoot);
|
|
1936
1906
|
const preferredProjectDir = encodeClaudeProjectDir(cwd);
|
|
1937
1907
|
const preferredPath = join11(projectsRoot, preferredProjectDir);
|
|
1938
|
-
if (
|
|
1908
|
+
if (existsSync9(preferredPath)) {
|
|
1939
1909
|
const hit = pickNewestJsonl(preferredPath, sessionId);
|
|
1940
1910
|
if (hit) {
|
|
1941
1911
|
return hit;
|
|
1942
1912
|
}
|
|
1943
1913
|
const legacySessions = join11(preferredPath, "sessions");
|
|
1944
|
-
if (
|
|
1914
|
+
if (existsSync9(legacySessions)) {
|
|
1945
1915
|
const legacyHit = pickNewestJsonl(legacySessions, sessionId);
|
|
1946
1916
|
if (legacyHit) {
|
|
1947
1917
|
return legacyHit;
|
|
@@ -1954,7 +1924,7 @@ function resolveSessionJsonlPath(repoRoot, options = {}) {
|
|
|
1954
1924
|
const projectPath = join11(projectsRoot, projectDir.name);
|
|
1955
1925
|
collectJsonlCandidates(projectPath, sessionId, candidates);
|
|
1956
1926
|
const legacySessions = join11(projectPath, "sessions");
|
|
1957
|
-
if (
|
|
1927
|
+
if (existsSync9(legacySessions)) {
|
|
1958
1928
|
collectJsonlCandidates(legacySessions, sessionId, candidates);
|
|
1959
1929
|
}
|
|
1960
1930
|
}
|
|
@@ -1974,7 +1944,7 @@ function pickNewestJsonl(dir, sessionId) {
|
|
|
1974
1944
|
return candidates[0]?.path ?? null;
|
|
1975
1945
|
}
|
|
1976
1946
|
function collectJsonlCandidates(dir, sessionId, out) {
|
|
1977
|
-
if (!
|
|
1947
|
+
if (!existsSync9(dir)) {
|
|
1978
1948
|
return;
|
|
1979
1949
|
}
|
|
1980
1950
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
@@ -2014,7 +1984,7 @@ async function runClaudeCodeCapture(repoRoot, cwd, dryRun, options) {
|
|
|
2014
1984
|
}
|
|
2015
1985
|
|
|
2016
1986
|
// src/capture/codebuddy/resolveSession.ts
|
|
2017
|
-
import { existsSync as
|
|
1987
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
2018
1988
|
import { homedir as homedir2 } from "os";
|
|
2019
1989
|
import { basename as basename3, join as join12, resolve as resolve4 } from "path";
|
|
2020
1990
|
function encodeCodebuddyProjectDir(absPath) {
|
|
@@ -2022,23 +1992,23 @@ function encodeCodebuddyProjectDir(absPath) {
|
|
|
2022
1992
|
}
|
|
2023
1993
|
function resolveCodebuddySessionJsonl(options) {
|
|
2024
1994
|
const override = process.env.HERMES_CODEBUDDY_SESSION;
|
|
2025
|
-
if (override &&
|
|
1995
|
+
if (override && existsSync10(override)) {
|
|
2026
1996
|
return resolve4(override);
|
|
2027
1997
|
}
|
|
2028
1998
|
const fromHook = options.transcriptPath;
|
|
2029
|
-
if (fromHook &&
|
|
1999
|
+
if (fromHook && existsSync10(fromHook)) {
|
|
2030
2000
|
return resolve4(fromHook);
|
|
2031
2001
|
}
|
|
2032
2002
|
const sessionId = process.env.CODEBUDDY_SESSION_ID ?? process.env.SESSION_ID;
|
|
2033
2003
|
const codebuddyHome = process.env.CODEBUDDY_CONFIG_DIR ? resolve4(process.env.CODEBUDDY_CONFIG_DIR) : join12(homedir2(), ".codebuddy");
|
|
2034
2004
|
const projectsRoot = join12(codebuddyHome, "projects");
|
|
2035
|
-
if (!
|
|
2005
|
+
if (!existsSync10(projectsRoot)) {
|
|
2036
2006
|
return null;
|
|
2037
2007
|
}
|
|
2038
2008
|
const cwd = resolve4(options.cwd ?? options.repoRoot);
|
|
2039
2009
|
const preferredProjectDir = encodeCodebuddyProjectDir(cwd);
|
|
2040
2010
|
const preferredPath = join12(projectsRoot, preferredProjectDir);
|
|
2041
|
-
if (
|
|
2011
|
+
if (existsSync10(preferredPath)) {
|
|
2042
2012
|
const hit = pickNewestJsonlRecursive(preferredPath, sessionId);
|
|
2043
2013
|
if (hit) {
|
|
2044
2014
|
return hit;
|
|
@@ -2067,7 +2037,7 @@ function pickNewestJsonlRecursive(dir, sessionId) {
|
|
|
2067
2037
|
return candidates[0]?.path ?? null;
|
|
2068
2038
|
}
|
|
2069
2039
|
function collectJsonlRecursive(dir, sessionId, out) {
|
|
2070
|
-
if (!
|
|
2040
|
+
if (!existsSync10(dir)) {
|
|
2071
2041
|
return;
|
|
2072
2042
|
}
|
|
2073
2043
|
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
@@ -2119,7 +2089,7 @@ async function runCodebuddyCapture(repoRoot, cwd, dryRun, options) {
|
|
|
2119
2089
|
}
|
|
2120
2090
|
|
|
2121
2091
|
// src/capture/cursor/resolveSession.ts
|
|
2122
|
-
import { existsSync as
|
|
2092
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
2123
2093
|
import { homedir as homedir3 } from "os";
|
|
2124
2094
|
import { join as join13, resolve as resolve5 } from "path";
|
|
2125
2095
|
function encodeCursorProjectDir(absPath) {
|
|
@@ -2127,12 +2097,12 @@ function encodeCursorProjectDir(absPath) {
|
|
|
2127
2097
|
}
|
|
2128
2098
|
function resolveCursorSessionJsonl(options) {
|
|
2129
2099
|
const override = process.env.HERMES_CURSOR_SESSION;
|
|
2130
|
-
if (override &&
|
|
2100
|
+
if (override && existsSync11(override)) {
|
|
2131
2101
|
return resolve5(override);
|
|
2132
2102
|
}
|
|
2133
2103
|
const cursorHome = process.env.CURSOR_CONFIG_DIR ? resolve5(process.env.CURSOR_CONFIG_DIR) : join13(homedir3(), ".cursor");
|
|
2134
2104
|
const projectsRoot = join13(cursorHome, "projects");
|
|
2135
|
-
if (!
|
|
2105
|
+
if (!existsSync11(projectsRoot)) {
|
|
2136
2106
|
return null;
|
|
2137
2107
|
}
|
|
2138
2108
|
const sessionId = options.hookInput?.sessionId ?? options.hookInput?.conversationId ?? process.env.CURSOR_SESSION_ID ?? process.env.CURSOR_AGENT_SESSION_ID;
|
|
@@ -2140,12 +2110,12 @@ function resolveCursorSessionJsonl(options) {
|
|
|
2140
2110
|
const encoded = encodeCursorProjectDir(workspace);
|
|
2141
2111
|
const projectDir = join13(projectsRoot, encoded);
|
|
2142
2112
|
const transcriptsRoot = join13(projectDir, "agent-transcripts");
|
|
2143
|
-
if (!
|
|
2113
|
+
if (!existsSync11(transcriptsRoot)) {
|
|
2144
2114
|
return pickNewestCursorJsonl(projectsRoot);
|
|
2145
2115
|
}
|
|
2146
2116
|
if (sessionId) {
|
|
2147
2117
|
const direct = join13(transcriptsRoot, sessionId, `${sessionId}.jsonl`);
|
|
2148
|
-
if (
|
|
2118
|
+
if (existsSync11(direct)) {
|
|
2149
2119
|
return direct;
|
|
2150
2120
|
}
|
|
2151
2121
|
const nested = findJsonlUnderDir(join13(transcriptsRoot, sessionId), sessionId);
|
|
@@ -2156,11 +2126,11 @@ function resolveCursorSessionJsonl(options) {
|
|
|
2156
2126
|
return pickNewestCursorJsonl(transcriptsRoot);
|
|
2157
2127
|
}
|
|
2158
2128
|
function findJsonlUnderDir(dir, sessionId) {
|
|
2159
|
-
if (!
|
|
2129
|
+
if (!existsSync11(dir)) {
|
|
2160
2130
|
return null;
|
|
2161
2131
|
}
|
|
2162
2132
|
const direct = join13(dir, `${sessionId}.jsonl`);
|
|
2163
|
-
if (
|
|
2133
|
+
if (existsSync11(direct)) {
|
|
2164
2134
|
return direct;
|
|
2165
2135
|
}
|
|
2166
2136
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
@@ -2186,7 +2156,7 @@ function pickNewestCursorJsonl(root) {
|
|
|
2186
2156
|
return candidates[0]?.path ?? null;
|
|
2187
2157
|
}
|
|
2188
2158
|
function collectJsonlRecursive2(dir, out) {
|
|
2189
|
-
if (!
|
|
2159
|
+
if (!existsSync11(dir)) {
|
|
2190
2160
|
return;
|
|
2191
2161
|
}
|
|
2192
2162
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
@@ -2415,7 +2385,7 @@ async function runFlushCommandCli(opts) {
|
|
|
2415
2385
|
}
|
|
2416
2386
|
|
|
2417
2387
|
// src/inject/runInject.ts
|
|
2418
|
-
import { existsSync as
|
|
2388
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync12 } from "fs";
|
|
2419
2389
|
import { join as join14 } from "path";
|
|
2420
2390
|
|
|
2421
2391
|
// src/inject/constants.ts
|
|
@@ -2476,9 +2446,9 @@ function rulesPathOnDisk(repoRoot) {
|
|
|
2476
2446
|
}
|
|
2477
2447
|
function readMemoryMd(repoRoot) {
|
|
2478
2448
|
const path = memoryPathOnDisk(repoRoot);
|
|
2479
|
-
if (!
|
|
2449
|
+
if (!existsSync12(path)) return null;
|
|
2480
2450
|
try {
|
|
2481
|
-
const content =
|
|
2451
|
+
const content = readFileSync12(path, "utf8");
|
|
2482
2452
|
return content.trim() || null;
|
|
2483
2453
|
} catch {
|
|
2484
2454
|
return null;
|
|
@@ -2497,7 +2467,7 @@ function readAllRules(repoRoot) {
|
|
|
2497
2467
|
for (const file of files) {
|
|
2498
2468
|
try {
|
|
2499
2469
|
const filePath = join14(rulesDir, file);
|
|
2500
|
-
const content =
|
|
2470
|
+
const content = readFileSync12(filePath, "utf8").trim();
|
|
2501
2471
|
if (!content) continue;
|
|
2502
2472
|
parts.push(`### ${file}`, "", content, "");
|
|
2503
2473
|
} catch {
|
|
@@ -2527,23 +2497,23 @@ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
|
2527
2497
|
import { join as join18 } from "path";
|
|
2528
2498
|
|
|
2529
2499
|
// src/init/mergeClaudeSettings.ts
|
|
2530
|
-
import { existsSync as
|
|
2500
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15 } from "fs";
|
|
2531
2501
|
import { join as join17 } from "path";
|
|
2532
2502
|
|
|
2533
2503
|
// src/init/templateDir.ts
|
|
2534
|
-
import { existsSync as
|
|
2504
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
|
|
2535
2505
|
import { dirname as dirname6, join as join16 } from "path";
|
|
2536
2506
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2537
2507
|
|
|
2538
2508
|
// src/index.ts
|
|
2539
|
-
import { readFileSync as
|
|
2509
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
2540
2510
|
import { dirname as dirname5, join as join15 } from "path";
|
|
2541
2511
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2542
2512
|
var PACKAGE_NAME = "@riconext/hermes-repo";
|
|
2543
2513
|
var __dirname = dirname5(fileURLToPath2(import.meta.url));
|
|
2544
2514
|
function readPkgVersion() {
|
|
2545
2515
|
const pkgPath = join15(__dirname, "..", "package.json");
|
|
2546
|
-
const pkg = JSON.parse(
|
|
2516
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
2547
2517
|
return pkg.version;
|
|
2548
2518
|
}
|
|
2549
2519
|
|
|
@@ -2555,7 +2525,7 @@ function resolveTemplateDir() {
|
|
|
2555
2525
|
join16(here, "..", "..", "templates")
|
|
2556
2526
|
];
|
|
2557
2527
|
for (const dir of candidates) {
|
|
2558
|
-
if (
|
|
2528
|
+
if (existsSync13(dir)) {
|
|
2559
2529
|
return dir;
|
|
2560
2530
|
}
|
|
2561
2531
|
}
|
|
@@ -2566,7 +2536,7 @@ function resolveTemplatePath(name) {
|
|
|
2566
2536
|
return join16(templateDir, name);
|
|
2567
2537
|
}
|
|
2568
2538
|
function readTemplate(name) {
|
|
2569
|
-
return
|
|
2539
|
+
return readFileSync14(resolveTemplatePath(name), "utf8");
|
|
2570
2540
|
}
|
|
2571
2541
|
function renderTemplate(name) {
|
|
2572
2542
|
const raw = readTemplate(name);
|
|
@@ -2580,12 +2550,12 @@ function claudeSettingsLocalPath(repoRoot) {
|
|
|
2580
2550
|
}
|
|
2581
2551
|
function mergeClaudeLocalSettings(repoRoot) {
|
|
2582
2552
|
const settingsPath = claudeSettingsLocalPath(repoRoot);
|
|
2583
|
-
const existed =
|
|
2553
|
+
const existed = existsSync14(settingsPath);
|
|
2584
2554
|
const templateParsed = JSON.parse(renderTemplate("hooks.json.tpl"));
|
|
2585
2555
|
let existing = {};
|
|
2586
2556
|
if (existed) {
|
|
2587
2557
|
try {
|
|
2588
|
-
existing = JSON.parse(
|
|
2558
|
+
existing = JSON.parse(readFileSync15(settingsPath, "utf8"));
|
|
2589
2559
|
} catch {
|
|
2590
2560
|
existing = {};
|
|
2591
2561
|
}
|
|
@@ -2625,7 +2595,7 @@ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
|
2625
2595
|
import { join as join20 } from "path";
|
|
2626
2596
|
|
|
2627
2597
|
// src/init/mergeCodexConfig.ts
|
|
2628
|
-
import { existsSync as
|
|
2598
|
+
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
2629
2599
|
import { join as join19 } from "path";
|
|
2630
2600
|
var CODEX_CONFIG_REL = ".codex/config.toml";
|
|
2631
2601
|
var CODEX_HERMES_START_MARKER = "# >>> hermes-repo codex (do not edit this block manually)";
|
|
@@ -2663,7 +2633,7 @@ ${after}` : ""}
|
|
|
2663
2633
|
}
|
|
2664
2634
|
function mergeCodexConfig(repoRoot) {
|
|
2665
2635
|
const configPath = codexConfigPath(repoRoot);
|
|
2666
|
-
const existed =
|
|
2636
|
+
const existed = existsSync15(configPath);
|
|
2667
2637
|
const block = buildCodexHermesBlock();
|
|
2668
2638
|
if (!existed) {
|
|
2669
2639
|
return {
|
|
@@ -2672,7 +2642,7 @@ function mergeCodexConfig(repoRoot) {
|
|
|
2672
2642
|
action: "created"
|
|
2673
2643
|
};
|
|
2674
2644
|
}
|
|
2675
|
-
const existing =
|
|
2645
|
+
const existing = readFileSync16(configPath, "utf8");
|
|
2676
2646
|
const hasBlock = existing.includes(CODEX_HERMES_START_MARKER) && existing.includes(CODEX_HERMES_END_MARKER);
|
|
2677
2647
|
return {
|
|
2678
2648
|
content: spliceHermesBlock(existing, block),
|
|
@@ -2699,7 +2669,7 @@ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
|
2699
2669
|
import { join as join22 } from "path";
|
|
2700
2670
|
|
|
2701
2671
|
// src/init/mergeCodebuddySettings.ts
|
|
2702
|
-
import { existsSync as
|
|
2672
|
+
import { existsSync as existsSync16, readFileSync as readFileSync17 } from "fs";
|
|
2703
2673
|
import { join as join21 } from "path";
|
|
2704
2674
|
var CODEBUDDY_SETTINGS_LOCAL_REL = ".codebuddy/settings.local.json";
|
|
2705
2675
|
function codebuddySettingsLocalPath(repoRoot) {
|
|
@@ -2707,14 +2677,14 @@ function codebuddySettingsLocalPath(repoRoot) {
|
|
|
2707
2677
|
}
|
|
2708
2678
|
function mergeCodebuddyLocalSettings(repoRoot) {
|
|
2709
2679
|
const settingsPath = codebuddySettingsLocalPath(repoRoot);
|
|
2710
|
-
const existed =
|
|
2680
|
+
const existed = existsSync16(settingsPath);
|
|
2711
2681
|
const templateParsed = JSON.parse(
|
|
2712
2682
|
renderTemplate("hooks.codebuddy.json.tpl")
|
|
2713
2683
|
);
|
|
2714
2684
|
let existing = {};
|
|
2715
2685
|
if (existed) {
|
|
2716
2686
|
try {
|
|
2717
|
-
existing = JSON.parse(
|
|
2687
|
+
existing = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
2718
2688
|
} catch {
|
|
2719
2689
|
existing = {};
|
|
2720
2690
|
}
|
|
@@ -2754,7 +2724,7 @@ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
|
2754
2724
|
import { join as join24 } from "path";
|
|
2755
2725
|
|
|
2756
2726
|
// src/init/mergeCursorHooks.ts
|
|
2757
|
-
import { existsSync as
|
|
2727
|
+
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "fs";
|
|
2758
2728
|
import { join as join23 } from "path";
|
|
2759
2729
|
var CURSOR_HOOKS_REL = ".cursor/hooks.json";
|
|
2760
2730
|
function cursorHooksPath(repoRoot) {
|
|
@@ -2762,12 +2732,12 @@ function cursorHooksPath(repoRoot) {
|
|
|
2762
2732
|
}
|
|
2763
2733
|
function mergeCursorHooks(repoRoot) {
|
|
2764
2734
|
const hooksPath = cursorHooksPath(repoRoot);
|
|
2765
|
-
const existed =
|
|
2735
|
+
const existed = existsSync17(hooksPath);
|
|
2766
2736
|
const templateParsed = JSON.parse(renderTemplate("hooks.cursor.json.tpl"));
|
|
2767
2737
|
let existing = {};
|
|
2768
2738
|
if (existed) {
|
|
2769
2739
|
try {
|
|
2770
|
-
existing = JSON.parse(
|
|
2740
|
+
existing = JSON.parse(readFileSync18(hooksPath, "utf8"));
|
|
2771
2741
|
} catch {
|
|
2772
2742
|
existing = {};
|
|
2773
2743
|
}
|
|
@@ -2874,14 +2844,14 @@ function ensureMemoryTree(repoRoot) {
|
|
|
2874
2844
|
}
|
|
2875
2845
|
|
|
2876
2846
|
// src/init/mergeAssistants.ts
|
|
2877
|
-
import { existsSync as
|
|
2847
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19 } from "fs";
|
|
2878
2848
|
function readExistingAssistants(repoRoot) {
|
|
2879
2849
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
2880
|
-
if (!
|
|
2850
|
+
if (!existsSync18(configPath)) {
|
|
2881
2851
|
return [];
|
|
2882
2852
|
}
|
|
2883
2853
|
try {
|
|
2884
|
-
const config = JSON.parse(
|
|
2854
|
+
const config = JSON.parse(readFileSync19(configPath, "utf8"));
|
|
2885
2855
|
if (!Array.isArray(config.assistants)) {
|
|
2886
2856
|
return [];
|
|
2887
2857
|
}
|
|
@@ -2896,16 +2866,16 @@ function mergeAssistants(repoRoot, selected) {
|
|
|
2896
2866
|
}
|
|
2897
2867
|
|
|
2898
2868
|
// src/init/mergeGitignore.ts
|
|
2899
|
-
import { existsSync as
|
|
2869
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
2900
2870
|
import { join as join26 } from "path";
|
|
2901
2871
|
var START_MARKER = "# >>> hermes-repo memory (do not edit this block manually)";
|
|
2902
2872
|
var END_MARKER = "# <<< hermes-repo memory";
|
|
2903
2873
|
function mergeHermesGitignore(repoRoot) {
|
|
2904
2874
|
const block = readTemplate("gitignore-block.txt").trimEnd() + "\n";
|
|
2905
2875
|
const gitignorePath = join26(repoRoot, ".gitignore");
|
|
2906
|
-
const contentBefore =
|
|
2876
|
+
const contentBefore = existsSync19(gitignorePath) ? readFileSync20(gitignorePath, "utf8") : "";
|
|
2907
2877
|
const warnBroadMemoryIgnore = contentBefore.length > 0 && !contentBefore.includes(START_MARKER) && /(^|\n)\.memory\/\s*$/m.test(contentBefore);
|
|
2908
|
-
if (!
|
|
2878
|
+
if (!existsSync19(gitignorePath)) {
|
|
2909
2879
|
writeFileSync11(gitignorePath, `${block}
|
|
2910
2880
|
`, "utf8");
|
|
2911
2881
|
return { action: "created", warnBroadMemoryIgnore: false };
|
|
@@ -2937,28 +2907,45 @@ function mergeHermesGitignore(repoRoot) {
|
|
|
2937
2907
|
import { copyFileSync, mkdirSync as mkdirSync12, writeFileSync as writeFileSync14 } from "fs";
|
|
2938
2908
|
|
|
2939
2909
|
// src/init/mergeConfig.ts
|
|
2940
|
-
import { existsSync as
|
|
2910
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "fs";
|
|
2911
|
+
var DEFAULT_LLM = {
|
|
2912
|
+
enabled: false,
|
|
2913
|
+
provider: "openai",
|
|
2914
|
+
baseUrl: "https://api.openai.com/v1",
|
|
2915
|
+
model: "gpt-4o",
|
|
2916
|
+
apiKey: "",
|
|
2917
|
+
timeoutMs: 6e4,
|
|
2918
|
+
maxInputChars: 24e3,
|
|
2919
|
+
mode: "async"
|
|
2920
|
+
};
|
|
2921
|
+
var DEFAULT_CONSOLIDATE = {
|
|
2922
|
+
autoArchiveDays: 30
|
|
2923
|
+
};
|
|
2941
2924
|
function mergeConfigForInit(repoRoot, assistants) {
|
|
2942
2925
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
2943
|
-
const existed =
|
|
2926
|
+
const existed = existsSync20(configPath);
|
|
2944
2927
|
let existing = {};
|
|
2945
2928
|
if (existed) {
|
|
2946
2929
|
try {
|
|
2947
|
-
existing = JSON.parse(
|
|
2930
|
+
existing = JSON.parse(readFileSync21(configPath, "utf8"));
|
|
2948
2931
|
} catch {
|
|
2949
2932
|
existing = {};
|
|
2950
2933
|
}
|
|
2951
2934
|
}
|
|
2952
2935
|
const prevStorage = existing.storage && typeof existing.storage === "object" && !Array.isArray(existing.storage) ? existing.storage : {};
|
|
2936
|
+
const prevLlm = existing.llm && typeof existing.llm === "object" && !Array.isArray(existing.llm) ? existing.llm : {};
|
|
2937
|
+
const prevConsolidate = existing.consolidate && typeof existing.consolidate === "object" && !Array.isArray(existing.consolidate) ? existing.consolidate : {};
|
|
2953
2938
|
const merged = {
|
|
2954
2939
|
...existing,
|
|
2955
|
-
version:
|
|
2940
|
+
version: 2,
|
|
2956
2941
|
storage: {
|
|
2957
2942
|
...prevStorage,
|
|
2958
2943
|
backend: "file"
|
|
2959
2944
|
},
|
|
2960
2945
|
assistants,
|
|
2961
|
-
debug: existing.debug === true
|
|
2946
|
+
debug: existing.debug === true,
|
|
2947
|
+
llm: { ...DEFAULT_LLM, ...prevLlm },
|
|
2948
|
+
consolidate: { ...DEFAULT_CONSOLIDATE, ...prevConsolidate }
|
|
2962
2949
|
};
|
|
2963
2950
|
return {
|
|
2964
2951
|
content: `${JSON.stringify(merged, null, 2)}
|
|
@@ -2968,7 +2955,7 @@ function mergeConfigForInit(repoRoot, assistants) {
|
|
|
2968
2955
|
}
|
|
2969
2956
|
|
|
2970
2957
|
// src/init/mergeAgentsMd.ts
|
|
2971
|
-
import { existsSync as
|
|
2958
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
|
|
2972
2959
|
import { join as join27 } from "path";
|
|
2973
2960
|
var HERMES_AGENTS_START_MARKER = "<!-- >>> hermes-repo agents (do not edit this block manually) -->";
|
|
2974
2961
|
var HERMES_AGENTS_END_MARKER = "<!-- <<< hermes-repo agents -->";
|
|
@@ -3027,11 +3014,11 @@ function spliceHermesBlock2(existing, block) {
|
|
|
3027
3014
|
function mergeAgentsMd(repoRoot, force) {
|
|
3028
3015
|
const agentsPath = join27(repoRoot, "AGENTS.md");
|
|
3029
3016
|
const block = buildHermesAgentsMarkedBlock();
|
|
3030
|
-
if (!
|
|
3017
|
+
if (!existsSync21(agentsPath)) {
|
|
3031
3018
|
writeFileSync12(agentsPath, buildNewAgentsMd(), "utf8");
|
|
3032
3019
|
return "created";
|
|
3033
3020
|
}
|
|
3034
|
-
const content =
|
|
3021
|
+
const content = readFileSync22(agentsPath, "utf8");
|
|
3035
3022
|
if (agentsMdHasHermesBlock(content)) {
|
|
3036
3023
|
if (!force) {
|
|
3037
3024
|
return "skipped";
|
|
@@ -3047,9 +3034,9 @@ function mergeAgentsMd(repoRoot, force) {
|
|
|
3047
3034
|
}
|
|
3048
3035
|
|
|
3049
3036
|
// src/init/scaffoldWrite.ts
|
|
3050
|
-
import { existsSync as
|
|
3037
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync13 } from "fs";
|
|
3051
3038
|
function shouldWriteFile(absolutePath, force) {
|
|
3052
|
-
if (!
|
|
3039
|
+
if (!existsSync22(absolutePath)) {
|
|
3053
3040
|
return { write: true, action: "created" };
|
|
3054
3041
|
}
|
|
3055
3042
|
if (force) {
|
|
@@ -3139,15 +3126,15 @@ function writeScaffoldFiles(repoRoot, opts, report) {
|
|
|
3139
3126
|
|
|
3140
3127
|
// src/init/prompts.ts
|
|
3141
3128
|
import { checkbox, confirm, input } from "@inquirer/prompts";
|
|
3142
|
-
import { existsSync as
|
|
3129
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23 } from "fs";
|
|
3143
3130
|
import { resolve as resolve6 } from "path";
|
|
3144
3131
|
function isInitialized(targetDir) {
|
|
3145
3132
|
const configPath = memoryPath(targetDir, "config.json");
|
|
3146
|
-
if (!
|
|
3133
|
+
if (!existsSync23(configPath)) {
|
|
3147
3134
|
return false;
|
|
3148
3135
|
}
|
|
3149
3136
|
try {
|
|
3150
|
-
const config = JSON.parse(
|
|
3137
|
+
const config = JSON.parse(readFileSync23(configPath, "utf8"));
|
|
3151
3138
|
return typeof config.version === "number" && config.version >= 1;
|
|
3152
3139
|
} catch {
|
|
3153
3140
|
return false;
|