@riconext/hermes-repo 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +27 -18
- package/dist/cli.js +303 -219
- package/dist/cli.js.map +1 -1
- package/dist/templates/AGENTS.md.tpl +11 -5
- 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,14 +102,26 @@ 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) {
|
|
110
115
|
const c = raw.consolidate;
|
|
116
|
+
const autoFlush = c?.autoFlush && typeof c.autoFlush === "object" && !Array.isArray(c.autoFlush) ? c.autoFlush : {};
|
|
111
117
|
return {
|
|
112
|
-
autoArchiveDays: typeof c?.autoArchiveDays === "number" ? c.autoArchiveDays : 30
|
|
118
|
+
autoArchiveDays: typeof c?.autoArchiveDays === "number" ? c.autoArchiveDays : 30,
|
|
119
|
+
autoFlush: {
|
|
120
|
+
enabled: autoFlush.enabled === true,
|
|
121
|
+
minPendingSessions: typeof autoFlush.minPendingSessions === "number" && autoFlush.minPendingSessions > 0 ? autoFlush.minPendingSessions : 3,
|
|
122
|
+
minIntervalMinutes: typeof autoFlush.minIntervalMinutes === "number" && autoFlush.minIntervalMinutes > 0 ? autoFlush.minIntervalMinutes : 30,
|
|
123
|
+
maxPendingChars: typeof autoFlush.maxPendingChars === "number" && autoFlush.maxPendingChars > 0 ? autoFlush.maxPendingChars : 2e4
|
|
124
|
+
}
|
|
113
125
|
};
|
|
114
126
|
}
|
|
115
127
|
function readConfigAtRepo(repoRoot) {
|
|
@@ -136,8 +148,25 @@ function readConfigAtRepo(repoRoot) {
|
|
|
136
148
|
storage: { backend: "file" },
|
|
137
149
|
assistants,
|
|
138
150
|
debug: raw.debug === true,
|
|
139
|
-
llm: {
|
|
140
|
-
|
|
151
|
+
llm: {
|
|
152
|
+
enabled: false,
|
|
153
|
+
provider: "openai",
|
|
154
|
+
baseUrl: "https://api.openai.com/v1",
|
|
155
|
+
model: "gpt-4o",
|
|
156
|
+
apiKey: "",
|
|
157
|
+
timeoutMs: 6e4,
|
|
158
|
+
maxInputChars: 24e3,
|
|
159
|
+
mode: "async"
|
|
160
|
+
},
|
|
161
|
+
consolidate: {
|
|
162
|
+
autoArchiveDays: 30,
|
|
163
|
+
autoFlush: {
|
|
164
|
+
enabled: false,
|
|
165
|
+
minPendingSessions: 3,
|
|
166
|
+
minIntervalMinutes: 30,
|
|
167
|
+
maxPendingChars: 2e4
|
|
168
|
+
}
|
|
169
|
+
}
|
|
141
170
|
};
|
|
142
171
|
}
|
|
143
172
|
return null;
|
|
@@ -158,12 +187,10 @@ function loadRepoContext(cwd) {
|
|
|
158
187
|
}
|
|
159
188
|
|
|
160
189
|
// src/capture/runLlmJob.ts
|
|
161
|
-
import { existsSync as
|
|
190
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
162
191
|
import { join as join6 } from "path";
|
|
163
192
|
|
|
164
193
|
// src/config/llmConfig.ts
|
|
165
|
-
var DEFAULT_LLM_TIMEOUT_MS = 6e4;
|
|
166
|
-
var DEFAULT_LLM_MAX_INPUT_CHARS = 24e3;
|
|
167
194
|
function isLlmAvailable(cfg) {
|
|
168
195
|
if (!cfg?.enabled) {
|
|
169
196
|
return false;
|
|
@@ -171,52 +198,11 @@ function isLlmAvailable(cfg) {
|
|
|
171
198
|
return Boolean(cfg.apiKey?.trim()) && Boolean(cfg.baseUrl?.trim()) && Boolean(cfg.model?.trim());
|
|
172
199
|
}
|
|
173
200
|
function effectiveLlmMode(cfg) {
|
|
174
|
-
const forceSync = process.env.HERMES_LLM_SYNC;
|
|
175
|
-
if (forceSync === "1" || forceSync === "true") {
|
|
176
|
-
return "sync";
|
|
177
|
-
}
|
|
178
201
|
return cfg.mode === "sync" ? "sync" : "async";
|
|
179
202
|
}
|
|
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
203
|
|
|
218
204
|
// src/capture/claude-code/parseJsonl.ts
|
|
219
|
-
import { readFileSync as
|
|
205
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
220
206
|
import { basename } from "path";
|
|
221
207
|
var FILE_CHANGE_TOOLS = /^(Write|Edit|MultiEdit|NotebookEdit|write|edit)$/i;
|
|
222
208
|
var SKIP_LINE_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -318,7 +304,7 @@ function countNestedTools(record) {
|
|
|
318
304
|
}
|
|
319
305
|
function parseJsonlFile(jsonlPath) {
|
|
320
306
|
const sessionId = basename(jsonlPath, ".jsonl");
|
|
321
|
-
const raw =
|
|
307
|
+
const raw = readFileSync2(jsonlPath, "utf8");
|
|
322
308
|
const messages = [];
|
|
323
309
|
let fileChanges = 0;
|
|
324
310
|
let toolCalls = 0;
|
|
@@ -362,10 +348,10 @@ function parseJsonlFile(jsonlPath) {
|
|
|
362
348
|
// src/capture/enqueueLlmJob.ts
|
|
363
349
|
import { spawn } from "child_process";
|
|
364
350
|
import {
|
|
365
|
-
existsSync as
|
|
351
|
+
existsSync as existsSync2,
|
|
366
352
|
mkdirSync as mkdirSync2,
|
|
367
353
|
readdirSync,
|
|
368
|
-
readFileSync as
|
|
354
|
+
readFileSync as readFileSync3,
|
|
369
355
|
rmSync,
|
|
370
356
|
writeFileSync
|
|
371
357
|
} from "fs";
|
|
@@ -383,7 +369,7 @@ function makeJobId(sessionId) {
|
|
|
383
369
|
}
|
|
384
370
|
function removeStaleJobsForSession(repoRoot, sessionId) {
|
|
385
371
|
const dir = pendingDir(repoRoot);
|
|
386
|
-
if (!
|
|
372
|
+
if (!existsSync2(dir)) {
|
|
387
373
|
return;
|
|
388
374
|
}
|
|
389
375
|
for (const name of readdirSync(dir)) {
|
|
@@ -392,7 +378,7 @@ function removeStaleJobsForSession(repoRoot, sessionId) {
|
|
|
392
378
|
}
|
|
393
379
|
try {
|
|
394
380
|
const raw = JSON.parse(
|
|
395
|
-
|
|
381
|
+
readFileSync3(join4(dir, name), "utf8")
|
|
396
382
|
);
|
|
397
383
|
if (raw.sessionId === sessionId) {
|
|
398
384
|
rmSync(join4(dir, name), { force: true });
|
|
@@ -436,24 +422,24 @@ function enqueueLlmJob(opts) {
|
|
|
436
422
|
}
|
|
437
423
|
function readLlmJob(repoRoot, jobId) {
|
|
438
424
|
const path = join4(pendingDir(repoRoot), `${jobId}.json`);
|
|
439
|
-
if (!
|
|
425
|
+
if (!existsSync2(path)) {
|
|
440
426
|
return null;
|
|
441
427
|
}
|
|
442
428
|
try {
|
|
443
|
-
return JSON.parse(
|
|
429
|
+
return JSON.parse(readFileSync3(path, "utf8"));
|
|
444
430
|
} catch {
|
|
445
431
|
return null;
|
|
446
432
|
}
|
|
447
433
|
}
|
|
448
434
|
function deleteLlmJob(repoRoot, jobId) {
|
|
449
435
|
const path = join4(pendingDir(repoRoot), `${jobId}.json`);
|
|
450
|
-
if (
|
|
436
|
+
if (existsSync2(path)) {
|
|
451
437
|
rmSync(path, { force: true });
|
|
452
438
|
}
|
|
453
439
|
}
|
|
454
440
|
function listPendingJobs(repoRoot) {
|
|
455
441
|
const dir = pendingDir(repoRoot);
|
|
456
|
-
if (!
|
|
442
|
+
if (!existsSync2(dir)) {
|
|
457
443
|
return [];
|
|
458
444
|
}
|
|
459
445
|
const jobs = [];
|
|
@@ -463,7 +449,7 @@ function listPendingJobs(repoRoot) {
|
|
|
463
449
|
}
|
|
464
450
|
try {
|
|
465
451
|
jobs.push(
|
|
466
|
-
JSON.parse(
|
|
452
|
+
JSON.parse(readFileSync3(join4(dir, name), "utf8"))
|
|
467
453
|
);
|
|
468
454
|
} catch {
|
|
469
455
|
}
|
|
@@ -693,7 +679,7 @@ async function llmFormat(session, assistant, llm) {
|
|
|
693
679
|
}
|
|
694
680
|
|
|
695
681
|
// src/capture/writeCapture.ts
|
|
696
|
-
import { existsSync as
|
|
682
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
697
683
|
import { join as join5 } from "path";
|
|
698
684
|
function isoNow() {
|
|
699
685
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -746,7 +732,7 @@ function resolveSessionFile(repoRoot, sessionId) {
|
|
|
746
732
|
const filename = `session-${sessionId}.md`;
|
|
747
733
|
const absolutePath = memoryPath(repoRoot, "captures", "raw", filename);
|
|
748
734
|
const relativePath = join5(".memory", "captures", "raw", filename);
|
|
749
|
-
return { absolutePath, relativePath, exists:
|
|
735
|
+
return { absolutePath, relativePath, exists: existsSync3(absolutePath) };
|
|
750
736
|
}
|
|
751
737
|
function renderCaptureMarkdown(formatted, date) {
|
|
752
738
|
const tagsStr = formatted.tags.map((t) => JSON.stringify(t)).join(", ");
|
|
@@ -793,7 +779,7 @@ function appendCaptureToSession(repoRoot, formatted) {
|
|
|
793
779
|
previousStatus: null
|
|
794
780
|
};
|
|
795
781
|
}
|
|
796
|
-
const existingContent =
|
|
782
|
+
const existingContent = readFileSync4(absolutePath, "utf8");
|
|
797
783
|
const existingFm = parseSessionFileFrontmatter(existingContent);
|
|
798
784
|
if (!existingFm) {
|
|
799
785
|
const fm = {
|
|
@@ -847,7 +833,7 @@ function appendCaptureToSession(repoRoot, formatted) {
|
|
|
847
833
|
function markSessionConsolidated(repoRoot, sessionId) {
|
|
848
834
|
const { absolutePath, exists } = resolveSessionFile(repoRoot, sessionId);
|
|
849
835
|
if (!exists) return;
|
|
850
|
-
const content =
|
|
836
|
+
const content = readFileSync4(absolutePath, "utf8");
|
|
851
837
|
const fm = parseSessionFileFrontmatter(content);
|
|
852
838
|
if (!fm) return;
|
|
853
839
|
const updatedFm = {
|
|
@@ -867,10 +853,10 @@ function markSessionConsolidated(repoRoot, sessionId) {
|
|
|
867
853
|
// src/capture/runLlmJob.ts
|
|
868
854
|
function captureAlreadyUpgraded(repoRoot, captureFile) {
|
|
869
855
|
const path = join6(repoRoot, captureFile);
|
|
870
|
-
if (!
|
|
856
|
+
if (!existsSync4(path)) {
|
|
871
857
|
return false;
|
|
872
858
|
}
|
|
873
|
-
const text =
|
|
859
|
+
const text = readFileSync5(path, "utf8");
|
|
874
860
|
return /llmUpgradedAt:/.test(text);
|
|
875
861
|
}
|
|
876
862
|
async function runLlmJob(repoRoot, job, debug) {
|
|
@@ -878,11 +864,11 @@ async function runLlmJob(repoRoot, job, debug) {
|
|
|
878
864
|
deleteLlmJob(repoRoot, job.jobId);
|
|
879
865
|
return { ok: true, reason: "already-upgraded" };
|
|
880
866
|
}
|
|
881
|
-
const llm =
|
|
867
|
+
const llm = readConfigAtRepo(repoRoot)?.llm ?? null;
|
|
882
868
|
if (!isLlmAvailable(llm)) {
|
|
883
869
|
return { ok: false, reason: "llm not available" };
|
|
884
870
|
}
|
|
885
|
-
if (!
|
|
871
|
+
if (!existsSync4(job.jsonlPath)) {
|
|
886
872
|
return { ok: false, reason: "jsonl missing" };
|
|
887
873
|
}
|
|
888
874
|
const session = parseJsonlFile(job.jsonlPath);
|
|
@@ -965,7 +951,7 @@ function runCaptureLlmCommand(opts) {
|
|
|
965
951
|
}
|
|
966
952
|
|
|
967
953
|
// src/capture/hookInput.ts
|
|
968
|
-
import { existsSync as
|
|
954
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
969
955
|
import { resolve as resolve2 } from "path";
|
|
970
956
|
function pickString(obj, ...keys) {
|
|
971
957
|
for (const key of keys) {
|
|
@@ -1003,7 +989,7 @@ function parseHookInputJson(raw) {
|
|
|
1003
989
|
try {
|
|
1004
990
|
const parsed = JSON.parse(trimmed);
|
|
1005
991
|
const transcriptRaw = pickString(parsed, "transcript_path", "transcriptPath");
|
|
1006
|
-
const transcriptPath = transcriptRaw &&
|
|
992
|
+
const transcriptPath = transcriptRaw && existsSync5(transcriptRaw) ? resolve2(transcriptRaw) : void 0;
|
|
1007
993
|
return {
|
|
1008
994
|
transcriptPath,
|
|
1009
995
|
transcriptPathRaw: transcriptRaw,
|
|
@@ -1022,7 +1008,7 @@ function readHookInputSync() {
|
|
|
1022
1008
|
return null;
|
|
1023
1009
|
}
|
|
1024
1010
|
try {
|
|
1025
|
-
const raw =
|
|
1011
|
+
const raw = readFileSync6(0, "utf8");
|
|
1026
1012
|
return parseHookInputJson(raw);
|
|
1027
1013
|
} catch {
|
|
1028
1014
|
return null;
|
|
@@ -1135,11 +1121,16 @@ function needsLlm(session) {
|
|
|
1135
1121
|
return false;
|
|
1136
1122
|
}
|
|
1137
1123
|
|
|
1124
|
+
// src/consolidate/scheduleConsolidate.ts
|
|
1125
|
+
import { spawn as spawn2 } from "child_process";
|
|
1126
|
+
import { dirname as dirname5, join as join11 } from "path";
|
|
1127
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1128
|
+
|
|
1138
1129
|
// src/consolidate/state.ts
|
|
1139
1130
|
import {
|
|
1140
|
-
existsSync as
|
|
1131
|
+
existsSync as existsSync6,
|
|
1141
1132
|
mkdirSync as mkdirSync4,
|
|
1142
|
-
readFileSync as
|
|
1133
|
+
readFileSync as readFileSync7,
|
|
1143
1134
|
rmSync as rmSync2,
|
|
1144
1135
|
writeFileSync as writeFileSync4
|
|
1145
1136
|
} from "fs";
|
|
@@ -1161,11 +1152,11 @@ function consolidateLockPath(repoRoot) {
|
|
|
1161
1152
|
}
|
|
1162
1153
|
function readConsolidateState(repoRoot) {
|
|
1163
1154
|
const path = consolidateStatePath(repoRoot);
|
|
1164
|
-
if (!
|
|
1155
|
+
if (!existsSync6(path)) {
|
|
1165
1156
|
return { ...EMPTY_STATE, processedSessions: {} };
|
|
1166
1157
|
}
|
|
1167
1158
|
try {
|
|
1168
|
-
const raw = JSON.parse(
|
|
1159
|
+
const raw = JSON.parse(readFileSync7(path, "utf8"));
|
|
1169
1160
|
if (typeof raw === "object" && raw !== null) {
|
|
1170
1161
|
const obj = raw;
|
|
1171
1162
|
if (obj.version === 2 && typeof obj.processedSessions === "object") {
|
|
@@ -1189,11 +1180,11 @@ function writeConsolidateState(repoRoot, state) {
|
|
|
1189
1180
|
}
|
|
1190
1181
|
function readConsolidateLock(repoRoot) {
|
|
1191
1182
|
const path = consolidateLockPath(repoRoot);
|
|
1192
|
-
if (!
|
|
1183
|
+
if (!existsSync6(path)) {
|
|
1193
1184
|
return null;
|
|
1194
1185
|
}
|
|
1195
1186
|
try {
|
|
1196
|
-
return JSON.parse(
|
|
1187
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
1197
1188
|
} catch {
|
|
1198
1189
|
return null;
|
|
1199
1190
|
}
|
|
@@ -1213,7 +1204,7 @@ function writeConsolidateLock(repoRoot) {
|
|
|
1213
1204
|
}
|
|
1214
1205
|
function releaseConsolidateLock(repoRoot) {
|
|
1215
1206
|
const path = consolidateLockPath(repoRoot);
|
|
1216
|
-
if (
|
|
1207
|
+
if (existsSync6(path)) {
|
|
1217
1208
|
rmSync2(path, { force: true });
|
|
1218
1209
|
}
|
|
1219
1210
|
}
|
|
@@ -1226,7 +1217,7 @@ function isLockStale(lock, ttlMs) {
|
|
|
1226
1217
|
}
|
|
1227
1218
|
|
|
1228
1219
|
// src/consolidate/sessionScanner.ts
|
|
1229
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
1220
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
1230
1221
|
import { join as join7 } from "path";
|
|
1231
1222
|
function parseSessionFrontmatter(content) {
|
|
1232
1223
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -1257,7 +1248,7 @@ function scanAllSessions(repoRoot) {
|
|
|
1257
1248
|
for (const file of files) {
|
|
1258
1249
|
const absolutePath = join7(rawDir, file);
|
|
1259
1250
|
try {
|
|
1260
|
-
const content =
|
|
1251
|
+
const content = readFileSync8(absolutePath, "utf8");
|
|
1261
1252
|
const fm = parseSessionFrontmatter(content);
|
|
1262
1253
|
if (!fm || !fm.sessionId) continue;
|
|
1263
1254
|
const fmEndIndex = content.indexOf("---", 4);
|
|
@@ -1286,7 +1277,7 @@ function filterPendingSessions(sessions) {
|
|
|
1286
1277
|
}
|
|
1287
1278
|
|
|
1288
1279
|
// src/consolidate/llmConsolidateV2.ts
|
|
1289
|
-
import { readFileSync as
|
|
1280
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
|
|
1290
1281
|
import { join as join8 } from "path";
|
|
1291
1282
|
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
1283
|
|
|
@@ -1395,7 +1386,7 @@ function scanMarkdownDirectory(absoluteDir, relativePrefix, type, domain, result
|
|
|
1395
1386
|
}
|
|
1396
1387
|
for (const file of files) {
|
|
1397
1388
|
try {
|
|
1398
|
-
const content =
|
|
1389
|
+
const content = readFileSync9(join8(absoluteDir, file), "utf8");
|
|
1399
1390
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1400
1391
|
let title = file.replace(/\.md$/, "");
|
|
1401
1392
|
if (fmMatch) {
|
|
@@ -1421,7 +1412,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1421
1412
|
const pendingSessions = sessions.map((s) => ({
|
|
1422
1413
|
sessionId: s.sessionId,
|
|
1423
1414
|
status: s.frontmatter.status,
|
|
1424
|
-
content:
|
|
1415
|
+
content: readFileSync9(s.absolutePath, "utf8"),
|
|
1425
1416
|
captureCount: s.frontmatter.captureCount,
|
|
1426
1417
|
createdAt: s.frontmatter.createdAt
|
|
1427
1418
|
}));
|
|
@@ -1429,7 +1420,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1429
1420
|
const memoryPathAbs = memoryPath(repoRoot, "MEMORY.md");
|
|
1430
1421
|
let currentMemoryMd = null;
|
|
1431
1422
|
try {
|
|
1432
|
-
currentMemoryMd =
|
|
1423
|
+
currentMemoryMd = readFileSync9(memoryPathAbs, "utf8");
|
|
1433
1424
|
} catch {
|
|
1434
1425
|
}
|
|
1435
1426
|
return {
|
|
@@ -1439,15 +1430,14 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1439
1430
|
};
|
|
1440
1431
|
}
|
|
1441
1432
|
async function callLlmConsolidate(input2, llmConfig) {
|
|
1442
|
-
|
|
1443
|
-
if (!apiKey) {
|
|
1433
|
+
if (!llmConfig.enabled) {
|
|
1444
1434
|
throw new Error(
|
|
1445
|
-
"LLM \u672A\
|
|
1435
|
+
"LLM \u672A\u542F\u7528\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.enabled = true"
|
|
1446
1436
|
);
|
|
1447
1437
|
}
|
|
1448
|
-
if (!llmConfig.
|
|
1438
|
+
if (!llmConfig.apiKey.trim() || !llmConfig.baseUrl.trim() || !llmConfig.model.trim()) {
|
|
1449
1439
|
throw new Error(
|
|
1450
|
-
"LLM \u672A\
|
|
1440
|
+
"LLM \u672A\u914D\u7F6E\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.apiKey\u3001llm.baseUrl \u548C llm.model"
|
|
1451
1441
|
);
|
|
1452
1442
|
}
|
|
1453
1443
|
const url = `${llmConfig.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
@@ -1459,7 +1449,7 @@ async function callLlmConsolidate(input2, llmConfig) {
|
|
|
1459
1449
|
method: "POST",
|
|
1460
1450
|
headers: {
|
|
1461
1451
|
"Content-Type": "application/json",
|
|
1462
|
-
Authorization: `Bearer ${apiKey}`
|
|
1452
|
+
Authorization: `Bearer ${llmConfig.apiKey}`
|
|
1463
1453
|
},
|
|
1464
1454
|
body: JSON.stringify({
|
|
1465
1455
|
model: llmConfig.model,
|
|
@@ -1551,7 +1541,7 @@ function isSkippedEntry(raw) {
|
|
|
1551
1541
|
}
|
|
1552
1542
|
|
|
1553
1543
|
// src/consolidate/writeKnowledge.ts
|
|
1554
|
-
import { existsSync as
|
|
1544
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
1555
1545
|
import { dirname as dirname4 } from "path";
|
|
1556
1546
|
function writeKnowledgeFiles(repoRoot, files) {
|
|
1557
1547
|
const result = {
|
|
@@ -1565,7 +1555,7 @@ function writeKnowledgeFiles(repoRoot, files) {
|
|
|
1565
1555
|
const dir = dirname4(absolutePath);
|
|
1566
1556
|
mkdirSync5(dir, { recursive: true });
|
|
1567
1557
|
const content = serializeKnowledgeFile(kf.frontmatter, kf.body);
|
|
1568
|
-
const alreadyExists =
|
|
1558
|
+
const alreadyExists = existsSync7(absolutePath);
|
|
1569
1559
|
if (kf.action === "create" && alreadyExists) {
|
|
1570
1560
|
result.updated.push(kf.targetPath);
|
|
1571
1561
|
} else if (kf.action === "create") {
|
|
@@ -1585,7 +1575,7 @@ function writeKnowledgeFiles(repoRoot, files) {
|
|
|
1585
1575
|
}
|
|
1586
1576
|
function writeMemoryMd(repoRoot, memoryMd) {
|
|
1587
1577
|
const memoryPathAbs = memoryPath(repoRoot, "MEMORY.md");
|
|
1588
|
-
if (
|
|
1578
|
+
if (existsSync7(memoryPathAbs)) {
|
|
1589
1579
|
const existing = __require("fs").readFileSync(memoryPathAbs, "utf8");
|
|
1590
1580
|
const preserved = extractUserEditedSections(existing);
|
|
1591
1581
|
if (preserved.length > 0) {
|
|
@@ -1802,6 +1792,9 @@ function extractDomainsFromResults(files) {
|
|
|
1802
1792
|
var CONSOLIDATE_LOCK_TTL_MS = 30 * 60 * 1e3;
|
|
1803
1793
|
|
|
1804
1794
|
// src/consolidate/scheduleConsolidate.ts
|
|
1795
|
+
function cliPath2() {
|
|
1796
|
+
return join11(dirname5(fileURLToPath2(import.meta.url)), "..", "cli.js");
|
|
1797
|
+
}
|
|
1805
1798
|
async function runFlushCommand(opts) {
|
|
1806
1799
|
const ctx = loadRepoContext(opts.cwd);
|
|
1807
1800
|
if (!ctx) {
|
|
@@ -1836,11 +1829,83 @@ async function runFlushCommand(opts) {
|
|
|
1836
1829
|
debug: opts.debug ?? ctx.config.debug
|
|
1837
1830
|
});
|
|
1838
1831
|
}
|
|
1832
|
+
function shouldAutoFlush(sessions, consolidate, lastConsolidatedAt) {
|
|
1833
|
+
const autoFlush = consolidate.autoFlush;
|
|
1834
|
+
if (!autoFlush.enabled || sessions.length === 0) {
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1837
|
+
if (sessions.length >= autoFlush.minPendingSessions) {
|
|
1838
|
+
return true;
|
|
1839
|
+
}
|
|
1840
|
+
const pendingChars = sessions.reduce(
|
|
1841
|
+
(sum, session) => sum + session.bodyContent.length,
|
|
1842
|
+
0
|
|
1843
|
+
);
|
|
1844
|
+
if (pendingChars >= autoFlush.maxPendingChars) {
|
|
1845
|
+
return true;
|
|
1846
|
+
}
|
|
1847
|
+
const last = Date.parse(lastConsolidatedAt);
|
|
1848
|
+
if (Number.isNaN(last)) {
|
|
1849
|
+
return true;
|
|
1850
|
+
}
|
|
1851
|
+
const minIntervalMs = autoFlush.minIntervalMinutes * 60 * 1e3;
|
|
1852
|
+
return Date.now() - last >= minIntervalMs;
|
|
1853
|
+
}
|
|
1839
1854
|
function maybeScheduleConsolidate(opts) {
|
|
1855
|
+
const ctx = loadRepoContext(opts.repoRoot);
|
|
1856
|
+
if (!ctx) {
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const autoFlush = ctx.config.consolidate.autoFlush;
|
|
1860
|
+
if (!autoFlush.enabled) {
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
if (!isLlmAvailable(ctx.config.llm)) {
|
|
1864
|
+
debugLog(opts.debug === true, "consolidate", "auto flush skipped: llm not available");
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1840
1867
|
const lock = readConsolidateLock(opts.repoRoot);
|
|
1841
1868
|
if (lock && !isLockStale(lock, CONSOLIDATE_LOCK_TTL_MS)) {
|
|
1869
|
+
debugLog(opts.debug === true, "consolidate", "auto flush skipped: lock held");
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const pendingSessions = filterPendingSessions(scanAllSessions(opts.repoRoot));
|
|
1873
|
+
const state = readConsolidateState(opts.repoRoot);
|
|
1874
|
+
if (!shouldAutoFlush(
|
|
1875
|
+
pendingSessions,
|
|
1876
|
+
ctx.config.consolidate,
|
|
1877
|
+
state.lastConsolidatedAt
|
|
1878
|
+
)) {
|
|
1879
|
+
debugLog(
|
|
1880
|
+
opts.debug === true,
|
|
1881
|
+
"consolidate",
|
|
1882
|
+
`auto flush skipped: ${pendingSessions.length} pending session(s) below thresholds`
|
|
1883
|
+
);
|
|
1842
1884
|
return;
|
|
1843
1885
|
}
|
|
1886
|
+
try {
|
|
1887
|
+
const child = spawn2(
|
|
1888
|
+
process.execPath,
|
|
1889
|
+
[cliPath2(), "flush", "-C", opts.repoRoot],
|
|
1890
|
+
{
|
|
1891
|
+
detached: true,
|
|
1892
|
+
stdio: "ignore",
|
|
1893
|
+
cwd: opts.repoRoot
|
|
1894
|
+
}
|
|
1895
|
+
);
|
|
1896
|
+
child.unref();
|
|
1897
|
+
debugLog(
|
|
1898
|
+
opts.debug === true,
|
|
1899
|
+
"consolidate",
|
|
1900
|
+
`auto flush scheduled: ${pendingSessions.length} pending session(s)`
|
|
1901
|
+
);
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
debugLog(
|
|
1904
|
+
opts.debug === true,
|
|
1905
|
+
"consolidate",
|
|
1906
|
+
`auto flush spawn failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1844
1909
|
}
|
|
1845
1910
|
|
|
1846
1911
|
// src/capture/commitCapture.ts
|
|
@@ -1863,7 +1928,7 @@ async function commitCapture(opts) {
|
|
|
1863
1928
|
}
|
|
1864
1929
|
const result = appendCaptureToSession(repoRoot, formatted);
|
|
1865
1930
|
const captureFile = result.relativePath;
|
|
1866
|
-
const llm =
|
|
1931
|
+
const llm = readConfigAtRepo(repoRoot)?.llm ?? null;
|
|
1867
1932
|
if (!isLlmAvailable(llm) || !needsLlm(session)) {
|
|
1868
1933
|
debugLog(debug === true, "capture", `ok: ${captureFile} (format=simple)`);
|
|
1869
1934
|
return finishCapture(repoRoot, debug, {
|
|
@@ -1911,37 +1976,37 @@ async function commitCapture(opts) {
|
|
|
1911
1976
|
}
|
|
1912
1977
|
|
|
1913
1978
|
// src/capture/claude-code/resolveSession.ts
|
|
1914
|
-
import { existsSync as
|
|
1979
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync } from "fs";
|
|
1915
1980
|
import { homedir } from "os";
|
|
1916
|
-
import { basename as basename2, join as
|
|
1981
|
+
import { basename as basename2, join as join12, resolve as resolve3 } from "path";
|
|
1917
1982
|
function encodeClaudeProjectDir(absPath) {
|
|
1918
1983
|
return resolve3(absPath).replace(/\//g, "-");
|
|
1919
1984
|
}
|
|
1920
1985
|
function resolveSessionJsonlPath(repoRoot, options = {}) {
|
|
1921
1986
|
const override = process.env.HERMES_SESSION_JSONL;
|
|
1922
|
-
if (override &&
|
|
1987
|
+
if (override && existsSync9(override)) {
|
|
1923
1988
|
return resolve3(override);
|
|
1924
1989
|
}
|
|
1925
1990
|
const fromHook = options.transcriptPath;
|
|
1926
|
-
if (fromHook &&
|
|
1991
|
+
if (fromHook && existsSync9(fromHook)) {
|
|
1927
1992
|
return resolve3(fromHook);
|
|
1928
1993
|
}
|
|
1929
1994
|
const sessionId = process.env.CLAUDE_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? process.env.SESSION_ID;
|
|
1930
|
-
const claudeHome = process.env.CLAUDE_CONFIG_DIR ? resolve3(process.env.CLAUDE_CONFIG_DIR) :
|
|
1931
|
-
const projectsRoot =
|
|
1932
|
-
if (!
|
|
1995
|
+
const claudeHome = process.env.CLAUDE_CONFIG_DIR ? resolve3(process.env.CLAUDE_CONFIG_DIR) : join12(homedir(), ".claude");
|
|
1996
|
+
const projectsRoot = join12(claudeHome, "projects");
|
|
1997
|
+
if (!existsSync9(projectsRoot)) {
|
|
1933
1998
|
return null;
|
|
1934
1999
|
}
|
|
1935
2000
|
const cwd = resolve3(options.cwd ?? repoRoot);
|
|
1936
2001
|
const preferredProjectDir = encodeClaudeProjectDir(cwd);
|
|
1937
|
-
const preferredPath =
|
|
1938
|
-
if (
|
|
2002
|
+
const preferredPath = join12(projectsRoot, preferredProjectDir);
|
|
2003
|
+
if (existsSync9(preferredPath)) {
|
|
1939
2004
|
const hit = pickNewestJsonl(preferredPath, sessionId);
|
|
1940
2005
|
if (hit) {
|
|
1941
2006
|
return hit;
|
|
1942
2007
|
}
|
|
1943
|
-
const legacySessions =
|
|
1944
|
-
if (
|
|
2008
|
+
const legacySessions = join12(preferredPath, "sessions");
|
|
2009
|
+
if (existsSync9(legacySessions)) {
|
|
1945
2010
|
const legacyHit = pickNewestJsonl(legacySessions, sessionId);
|
|
1946
2011
|
if (legacyHit) {
|
|
1947
2012
|
return legacyHit;
|
|
@@ -1951,10 +2016,10 @@ function resolveSessionJsonlPath(repoRoot, options = {}) {
|
|
|
1951
2016
|
const candidates = [];
|
|
1952
2017
|
for (const projectDir of readdirSync4(projectsRoot, { withFileTypes: true })) {
|
|
1953
2018
|
if (!projectDir.isDirectory()) continue;
|
|
1954
|
-
const projectPath =
|
|
2019
|
+
const projectPath = join12(projectsRoot, projectDir.name);
|
|
1955
2020
|
collectJsonlCandidates(projectPath, sessionId, candidates);
|
|
1956
|
-
const legacySessions =
|
|
1957
|
-
if (
|
|
2021
|
+
const legacySessions = join12(projectPath, "sessions");
|
|
2022
|
+
if (existsSync9(legacySessions)) {
|
|
1958
2023
|
collectJsonlCandidates(legacySessions, sessionId, candidates);
|
|
1959
2024
|
}
|
|
1960
2025
|
}
|
|
@@ -1974,14 +2039,14 @@ function pickNewestJsonl(dir, sessionId) {
|
|
|
1974
2039
|
return candidates[0]?.path ?? null;
|
|
1975
2040
|
}
|
|
1976
2041
|
function collectJsonlCandidates(dir, sessionId, out) {
|
|
1977
|
-
if (!
|
|
2042
|
+
if (!existsSync9(dir)) {
|
|
1978
2043
|
return;
|
|
1979
2044
|
}
|
|
1980
2045
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
1981
2046
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
|
|
1982
2047
|
continue;
|
|
1983
2048
|
}
|
|
1984
|
-
const fullPath =
|
|
2049
|
+
const fullPath = join12(dir, entry.name);
|
|
1985
2050
|
if (sessionId && !entry.name.includes(sessionId) && basename2(entry.name, ".jsonl") !== sessionId) {
|
|
1986
2051
|
continue;
|
|
1987
2052
|
}
|
|
@@ -2014,31 +2079,31 @@ async function runClaudeCodeCapture(repoRoot, cwd, dryRun, options) {
|
|
|
2014
2079
|
}
|
|
2015
2080
|
|
|
2016
2081
|
// src/capture/codebuddy/resolveSession.ts
|
|
2017
|
-
import { existsSync as
|
|
2082
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
2018
2083
|
import { homedir as homedir2 } from "os";
|
|
2019
|
-
import { basename as basename3, join as
|
|
2084
|
+
import { basename as basename3, join as join13, resolve as resolve4 } from "path";
|
|
2020
2085
|
function encodeCodebuddyProjectDir(absPath) {
|
|
2021
2086
|
return resolve4(absPath).replace(/^\//, "").replace(/\//g, "-");
|
|
2022
2087
|
}
|
|
2023
2088
|
function resolveCodebuddySessionJsonl(options) {
|
|
2024
2089
|
const override = process.env.HERMES_CODEBUDDY_SESSION;
|
|
2025
|
-
if (override &&
|
|
2090
|
+
if (override && existsSync10(override)) {
|
|
2026
2091
|
return resolve4(override);
|
|
2027
2092
|
}
|
|
2028
2093
|
const fromHook = options.transcriptPath;
|
|
2029
|
-
if (fromHook &&
|
|
2094
|
+
if (fromHook && existsSync10(fromHook)) {
|
|
2030
2095
|
return resolve4(fromHook);
|
|
2031
2096
|
}
|
|
2032
2097
|
const sessionId = process.env.CODEBUDDY_SESSION_ID ?? process.env.SESSION_ID;
|
|
2033
|
-
const codebuddyHome = process.env.CODEBUDDY_CONFIG_DIR ? resolve4(process.env.CODEBUDDY_CONFIG_DIR) :
|
|
2034
|
-
const projectsRoot =
|
|
2035
|
-
if (!
|
|
2098
|
+
const codebuddyHome = process.env.CODEBUDDY_CONFIG_DIR ? resolve4(process.env.CODEBUDDY_CONFIG_DIR) : join13(homedir2(), ".codebuddy");
|
|
2099
|
+
const projectsRoot = join13(codebuddyHome, "projects");
|
|
2100
|
+
if (!existsSync10(projectsRoot)) {
|
|
2036
2101
|
return null;
|
|
2037
2102
|
}
|
|
2038
2103
|
const cwd = resolve4(options.cwd ?? options.repoRoot);
|
|
2039
2104
|
const preferredProjectDir = encodeCodebuddyProjectDir(cwd);
|
|
2040
|
-
const preferredPath =
|
|
2041
|
-
if (
|
|
2105
|
+
const preferredPath = join13(projectsRoot, preferredProjectDir);
|
|
2106
|
+
if (existsSync10(preferredPath)) {
|
|
2042
2107
|
const hit = pickNewestJsonlRecursive(preferredPath, sessionId);
|
|
2043
2108
|
if (hit) {
|
|
2044
2109
|
return hit;
|
|
@@ -2049,7 +2114,7 @@ function resolveCodebuddySessionJsonl(options) {
|
|
|
2049
2114
|
if (!projectDir.isDirectory()) {
|
|
2050
2115
|
continue;
|
|
2051
2116
|
}
|
|
2052
|
-
collectJsonlRecursive(
|
|
2117
|
+
collectJsonlRecursive(join13(projectsRoot, projectDir.name), sessionId, candidates);
|
|
2053
2118
|
}
|
|
2054
2119
|
if (candidates.length === 0) {
|
|
2055
2120
|
return null;
|
|
@@ -2067,11 +2132,11 @@ function pickNewestJsonlRecursive(dir, sessionId) {
|
|
|
2067
2132
|
return candidates[0]?.path ?? null;
|
|
2068
2133
|
}
|
|
2069
2134
|
function collectJsonlRecursive(dir, sessionId, out) {
|
|
2070
|
-
if (!
|
|
2135
|
+
if (!existsSync10(dir)) {
|
|
2071
2136
|
return;
|
|
2072
2137
|
}
|
|
2073
2138
|
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
2074
|
-
const full =
|
|
2139
|
+
const full = join13(dir, entry.name);
|
|
2075
2140
|
if (entry.isDirectory()) {
|
|
2076
2141
|
collectJsonlRecursive(full, sessionId, out);
|
|
2077
2142
|
continue;
|
|
@@ -2119,36 +2184,36 @@ async function runCodebuddyCapture(repoRoot, cwd, dryRun, options) {
|
|
|
2119
2184
|
}
|
|
2120
2185
|
|
|
2121
2186
|
// src/capture/cursor/resolveSession.ts
|
|
2122
|
-
import { existsSync as
|
|
2187
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
2123
2188
|
import { homedir as homedir3 } from "os";
|
|
2124
|
-
import { join as
|
|
2189
|
+
import { join as join14, resolve as resolve5 } from "path";
|
|
2125
2190
|
function encodeCursorProjectDir(absPath) {
|
|
2126
2191
|
return resolve5(absPath).replace(/^\//, "").replace(/\//g, "-");
|
|
2127
2192
|
}
|
|
2128
2193
|
function resolveCursorSessionJsonl(options) {
|
|
2129
2194
|
const override = process.env.HERMES_CURSOR_SESSION;
|
|
2130
|
-
if (override &&
|
|
2195
|
+
if (override && existsSync11(override)) {
|
|
2131
2196
|
return resolve5(override);
|
|
2132
2197
|
}
|
|
2133
|
-
const cursorHome = process.env.CURSOR_CONFIG_DIR ? resolve5(process.env.CURSOR_CONFIG_DIR) :
|
|
2134
|
-
const projectsRoot =
|
|
2135
|
-
if (!
|
|
2198
|
+
const cursorHome = process.env.CURSOR_CONFIG_DIR ? resolve5(process.env.CURSOR_CONFIG_DIR) : join14(homedir3(), ".cursor");
|
|
2199
|
+
const projectsRoot = join14(cursorHome, "projects");
|
|
2200
|
+
if (!existsSync11(projectsRoot)) {
|
|
2136
2201
|
return null;
|
|
2137
2202
|
}
|
|
2138
2203
|
const sessionId = options.hookInput?.sessionId ?? options.hookInput?.conversationId ?? process.env.CURSOR_SESSION_ID ?? process.env.CURSOR_AGENT_SESSION_ID;
|
|
2139
2204
|
const workspace = options.hookInput?.workspaceRoots?.[0] ?? (options.cwd ? resolve5(options.cwd) : resolve5(options.repoRoot));
|
|
2140
2205
|
const encoded = encodeCursorProjectDir(workspace);
|
|
2141
|
-
const projectDir =
|
|
2142
|
-
const transcriptsRoot =
|
|
2143
|
-
if (!
|
|
2206
|
+
const projectDir = join14(projectsRoot, encoded);
|
|
2207
|
+
const transcriptsRoot = join14(projectDir, "agent-transcripts");
|
|
2208
|
+
if (!existsSync11(transcriptsRoot)) {
|
|
2144
2209
|
return pickNewestCursorJsonl(projectsRoot);
|
|
2145
2210
|
}
|
|
2146
2211
|
if (sessionId) {
|
|
2147
|
-
const direct =
|
|
2148
|
-
if (
|
|
2212
|
+
const direct = join14(transcriptsRoot, sessionId, `${sessionId}.jsonl`);
|
|
2213
|
+
if (existsSync11(direct)) {
|
|
2149
2214
|
return direct;
|
|
2150
2215
|
}
|
|
2151
|
-
const nested = findJsonlUnderDir(
|
|
2216
|
+
const nested = findJsonlUnderDir(join14(transcriptsRoot, sessionId), sessionId);
|
|
2152
2217
|
if (nested) {
|
|
2153
2218
|
return nested;
|
|
2154
2219
|
}
|
|
@@ -2156,19 +2221,19 @@ function resolveCursorSessionJsonl(options) {
|
|
|
2156
2221
|
return pickNewestCursorJsonl(transcriptsRoot);
|
|
2157
2222
|
}
|
|
2158
2223
|
function findJsonlUnderDir(dir, sessionId) {
|
|
2159
|
-
if (!
|
|
2224
|
+
if (!existsSync11(dir)) {
|
|
2160
2225
|
return null;
|
|
2161
2226
|
}
|
|
2162
|
-
const direct =
|
|
2163
|
-
if (
|
|
2227
|
+
const direct = join14(dir, `${sessionId}.jsonl`);
|
|
2228
|
+
if (existsSync11(direct)) {
|
|
2164
2229
|
return direct;
|
|
2165
2230
|
}
|
|
2166
2231
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
2167
2232
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
2168
|
-
return
|
|
2233
|
+
return join14(dir, entry.name);
|
|
2169
2234
|
}
|
|
2170
2235
|
if (entry.isDirectory()) {
|
|
2171
|
-
const found = findJsonlUnderDir(
|
|
2236
|
+
const found = findJsonlUnderDir(join14(dir, entry.name), sessionId);
|
|
2172
2237
|
if (found) {
|
|
2173
2238
|
return found;
|
|
2174
2239
|
}
|
|
@@ -2186,11 +2251,11 @@ function pickNewestCursorJsonl(root) {
|
|
|
2186
2251
|
return candidates[0]?.path ?? null;
|
|
2187
2252
|
}
|
|
2188
2253
|
function collectJsonlRecursive2(dir, out) {
|
|
2189
|
-
if (!
|
|
2254
|
+
if (!existsSync11(dir)) {
|
|
2190
2255
|
return;
|
|
2191
2256
|
}
|
|
2192
2257
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
2193
|
-
const full =
|
|
2258
|
+
const full = join14(dir, entry.name);
|
|
2194
2259
|
if (entry.isDirectory()) {
|
|
2195
2260
|
collectJsonlRecursive2(full, out);
|
|
2196
2261
|
continue;
|
|
@@ -2415,8 +2480,8 @@ async function runFlushCommandCli(opts) {
|
|
|
2415
2480
|
}
|
|
2416
2481
|
|
|
2417
2482
|
// src/inject/runInject.ts
|
|
2418
|
-
import { existsSync as
|
|
2419
|
-
import { join as
|
|
2483
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync12 } from "fs";
|
|
2484
|
+
import { join as join15 } from "path";
|
|
2420
2485
|
|
|
2421
2486
|
// src/inject/constants.ts
|
|
2422
2487
|
var INJECT_MAX_CHARS = 8e3;
|
|
@@ -2476,9 +2541,9 @@ function rulesPathOnDisk(repoRoot) {
|
|
|
2476
2541
|
}
|
|
2477
2542
|
function readMemoryMd(repoRoot) {
|
|
2478
2543
|
const path = memoryPathOnDisk(repoRoot);
|
|
2479
|
-
if (!
|
|
2544
|
+
if (!existsSync12(path)) return null;
|
|
2480
2545
|
try {
|
|
2481
|
-
const content =
|
|
2546
|
+
const content = readFileSync12(path, "utf8");
|
|
2482
2547
|
return content.trim() || null;
|
|
2483
2548
|
} catch {
|
|
2484
2549
|
return null;
|
|
@@ -2496,8 +2561,8 @@ function readAllRules(repoRoot) {
|
|
|
2496
2561
|
const parts = [];
|
|
2497
2562
|
for (const file of files) {
|
|
2498
2563
|
try {
|
|
2499
|
-
const filePath =
|
|
2500
|
-
const content =
|
|
2564
|
+
const filePath = join15(rulesDir, file);
|
|
2565
|
+
const content = readFileSync12(filePath, "utf8").trim();
|
|
2501
2566
|
if (!content) continue;
|
|
2502
2567
|
parts.push(`### ${file}`, "", content, "");
|
|
2503
2568
|
} catch {
|
|
@@ -2524,49 +2589,49 @@ import { resolve as resolve7 } from "path";
|
|
|
2524
2589
|
|
|
2525
2590
|
// src/init/assistants/claude-code.ts
|
|
2526
2591
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
2527
|
-
import { join as
|
|
2592
|
+
import { join as join19 } from "path";
|
|
2528
2593
|
|
|
2529
2594
|
// src/init/mergeClaudeSettings.ts
|
|
2530
|
-
import { existsSync as
|
|
2531
|
-
import { join as
|
|
2595
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15 } from "fs";
|
|
2596
|
+
import { join as join18 } from "path";
|
|
2532
2597
|
|
|
2533
2598
|
// src/init/templateDir.ts
|
|
2534
|
-
import { existsSync as
|
|
2535
|
-
import { dirname as
|
|
2536
|
-
import { fileURLToPath as
|
|
2599
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
|
|
2600
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
2601
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2537
2602
|
|
|
2538
2603
|
// src/index.ts
|
|
2539
|
-
import { readFileSync as
|
|
2540
|
-
import { dirname as
|
|
2541
|
-
import { fileURLToPath as
|
|
2604
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
2605
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
2606
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2542
2607
|
var PACKAGE_NAME = "@riconext/hermes-repo";
|
|
2543
|
-
var __dirname =
|
|
2608
|
+
var __dirname = dirname6(fileURLToPath3(import.meta.url));
|
|
2544
2609
|
function readPkgVersion() {
|
|
2545
|
-
const pkgPath =
|
|
2546
|
-
const pkg = JSON.parse(
|
|
2610
|
+
const pkgPath = join16(__dirname, "..", "package.json");
|
|
2611
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
2547
2612
|
return pkg.version;
|
|
2548
2613
|
}
|
|
2549
2614
|
|
|
2550
2615
|
// src/init/templateDir.ts
|
|
2551
2616
|
function resolveTemplateDir() {
|
|
2552
|
-
const here =
|
|
2617
|
+
const here = dirname7(fileURLToPath4(import.meta.url));
|
|
2553
2618
|
const candidates = [
|
|
2554
|
-
|
|
2555
|
-
|
|
2619
|
+
join17(here, "templates"),
|
|
2620
|
+
join17(here, "..", "..", "templates")
|
|
2556
2621
|
];
|
|
2557
2622
|
for (const dir of candidates) {
|
|
2558
|
-
if (
|
|
2623
|
+
if (existsSync13(dir)) {
|
|
2559
2624
|
return dir;
|
|
2560
2625
|
}
|
|
2561
2626
|
}
|
|
2562
|
-
return
|
|
2627
|
+
return join17(here, "templates");
|
|
2563
2628
|
}
|
|
2564
2629
|
var templateDir = resolveTemplateDir();
|
|
2565
2630
|
function resolveTemplatePath(name) {
|
|
2566
|
-
return
|
|
2631
|
+
return join17(templateDir, name);
|
|
2567
2632
|
}
|
|
2568
2633
|
function readTemplate(name) {
|
|
2569
|
-
return
|
|
2634
|
+
return readFileSync14(resolveTemplatePath(name), "utf8");
|
|
2570
2635
|
}
|
|
2571
2636
|
function renderTemplate(name) {
|
|
2572
2637
|
const raw = readTemplate(name);
|
|
@@ -2576,16 +2641,16 @@ function renderTemplate(name) {
|
|
|
2576
2641
|
// src/init/mergeClaudeSettings.ts
|
|
2577
2642
|
var CLAUDE_SETTINGS_LOCAL_REL = ".claude/settings.local.json";
|
|
2578
2643
|
function claudeSettingsLocalPath(repoRoot) {
|
|
2579
|
-
return
|
|
2644
|
+
return join18(repoRoot, ".claude", "settings.local.json");
|
|
2580
2645
|
}
|
|
2581
2646
|
function mergeClaudeLocalSettings(repoRoot) {
|
|
2582
2647
|
const settingsPath = claudeSettingsLocalPath(repoRoot);
|
|
2583
|
-
const existed =
|
|
2648
|
+
const existed = existsSync14(settingsPath);
|
|
2584
2649
|
const templateParsed = JSON.parse(renderTemplate("hooks.json.tpl"));
|
|
2585
2650
|
let existing = {};
|
|
2586
2651
|
if (existed) {
|
|
2587
2652
|
try {
|
|
2588
|
-
existing = JSON.parse(
|
|
2653
|
+
existing = JSON.parse(readFileSync15(settingsPath, "utf8"));
|
|
2589
2654
|
} catch {
|
|
2590
2655
|
existing = {};
|
|
2591
2656
|
}
|
|
@@ -2613,7 +2678,7 @@ var claudeCodeAdapter = {
|
|
|
2613
2678
|
available: true,
|
|
2614
2679
|
scaffoldPaths: [CLAUDE_SETTINGS_LOCAL_REL],
|
|
2615
2680
|
write(ctx) {
|
|
2616
|
-
mkdirSync7(
|
|
2681
|
+
mkdirSync7(join19(ctx.repoRoot, ".claude"), { recursive: true });
|
|
2617
2682
|
const { content, action } = mergeClaudeLocalSettings(ctx.repoRoot);
|
|
2618
2683
|
writeFileSync6(claudeSettingsLocalPath(ctx.repoRoot), content, "utf8");
|
|
2619
2684
|
ctx.report.files.push({ path: CLAUDE_SETTINGS_LOCAL_REL, action });
|
|
@@ -2622,11 +2687,11 @@ var claudeCodeAdapter = {
|
|
|
2622
2687
|
|
|
2623
2688
|
// src/init/assistants/codex.ts
|
|
2624
2689
|
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
2625
|
-
import { join as
|
|
2690
|
+
import { join as join21 } from "path";
|
|
2626
2691
|
|
|
2627
2692
|
// src/init/mergeCodexConfig.ts
|
|
2628
|
-
import { existsSync as
|
|
2629
|
-
import { join as
|
|
2693
|
+
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
2694
|
+
import { join as join20 } from "path";
|
|
2630
2695
|
var CODEX_CONFIG_REL = ".codex/config.toml";
|
|
2631
2696
|
var CODEX_HERMES_START_MARKER = "# >>> hermes-repo codex (do not edit this block manually)";
|
|
2632
2697
|
var CODEX_HERMES_END_MARKER = "# <<< hermes-repo codex";
|
|
@@ -2640,7 +2705,7 @@ function buildCodexHermesBlock() {
|
|
|
2640
2705
|
].join("\n");
|
|
2641
2706
|
}
|
|
2642
2707
|
function codexConfigPath(repoRoot) {
|
|
2643
|
-
return
|
|
2708
|
+
return join20(repoRoot, ".codex", "config.toml");
|
|
2644
2709
|
}
|
|
2645
2710
|
function spliceHermesBlock(existing, block) {
|
|
2646
2711
|
const startIdx = existing.indexOf(CODEX_HERMES_START_MARKER);
|
|
@@ -2663,7 +2728,7 @@ ${after}` : ""}
|
|
|
2663
2728
|
}
|
|
2664
2729
|
function mergeCodexConfig(repoRoot) {
|
|
2665
2730
|
const configPath = codexConfigPath(repoRoot);
|
|
2666
|
-
const existed =
|
|
2731
|
+
const existed = existsSync15(configPath);
|
|
2667
2732
|
const block = buildCodexHermesBlock();
|
|
2668
2733
|
if (!existed) {
|
|
2669
2734
|
return {
|
|
@@ -2672,7 +2737,7 @@ function mergeCodexConfig(repoRoot) {
|
|
|
2672
2737
|
action: "created"
|
|
2673
2738
|
};
|
|
2674
2739
|
}
|
|
2675
|
-
const existing =
|
|
2740
|
+
const existing = readFileSync16(configPath, "utf8");
|
|
2676
2741
|
const hasBlock = existing.includes(CODEX_HERMES_START_MARKER) && existing.includes(CODEX_HERMES_END_MARKER);
|
|
2677
2742
|
return {
|
|
2678
2743
|
content: spliceHermesBlock(existing, block),
|
|
@@ -2687,7 +2752,7 @@ var codexAdapter = {
|
|
|
2687
2752
|
available: true,
|
|
2688
2753
|
scaffoldPaths: [CODEX_CONFIG_REL],
|
|
2689
2754
|
write(ctx) {
|
|
2690
|
-
mkdirSync8(
|
|
2755
|
+
mkdirSync8(join21(ctx.repoRoot, ".codex"), { recursive: true });
|
|
2691
2756
|
const { content, action } = mergeCodexConfig(ctx.repoRoot);
|
|
2692
2757
|
writeFileSync7(codexConfigPath(ctx.repoRoot), content, "utf8");
|
|
2693
2758
|
ctx.report.files.push({ path: CODEX_CONFIG_REL, action });
|
|
@@ -2696,25 +2761,25 @@ var codexAdapter = {
|
|
|
2696
2761
|
|
|
2697
2762
|
// src/init/assistants/codebuddy.ts
|
|
2698
2763
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
2699
|
-
import { join as
|
|
2764
|
+
import { join as join23 } from "path";
|
|
2700
2765
|
|
|
2701
2766
|
// src/init/mergeCodebuddySettings.ts
|
|
2702
|
-
import { existsSync as
|
|
2703
|
-
import { join as
|
|
2767
|
+
import { existsSync as existsSync16, readFileSync as readFileSync17 } from "fs";
|
|
2768
|
+
import { join as join22 } from "path";
|
|
2704
2769
|
var CODEBUDDY_SETTINGS_LOCAL_REL = ".codebuddy/settings.local.json";
|
|
2705
2770
|
function codebuddySettingsLocalPath(repoRoot) {
|
|
2706
|
-
return
|
|
2771
|
+
return join22(repoRoot, ".codebuddy", "settings.local.json");
|
|
2707
2772
|
}
|
|
2708
2773
|
function mergeCodebuddyLocalSettings(repoRoot) {
|
|
2709
2774
|
const settingsPath = codebuddySettingsLocalPath(repoRoot);
|
|
2710
|
-
const existed =
|
|
2775
|
+
const existed = existsSync16(settingsPath);
|
|
2711
2776
|
const templateParsed = JSON.parse(
|
|
2712
2777
|
renderTemplate("hooks.codebuddy.json.tpl")
|
|
2713
2778
|
);
|
|
2714
2779
|
let existing = {};
|
|
2715
2780
|
if (existed) {
|
|
2716
2781
|
try {
|
|
2717
|
-
existing = JSON.parse(
|
|
2782
|
+
existing = JSON.parse(readFileSync17(settingsPath, "utf8"));
|
|
2718
2783
|
} catch {
|
|
2719
2784
|
existing = {};
|
|
2720
2785
|
}
|
|
@@ -2742,7 +2807,7 @@ var codebuddyAdapter = {
|
|
|
2742
2807
|
available: true,
|
|
2743
2808
|
scaffoldPaths: [CODEBUDDY_SETTINGS_LOCAL_REL],
|
|
2744
2809
|
write(ctx) {
|
|
2745
|
-
mkdirSync9(
|
|
2810
|
+
mkdirSync9(join23(ctx.repoRoot, ".codebuddy"), { recursive: true });
|
|
2746
2811
|
const { content, action } = mergeCodebuddyLocalSettings(ctx.repoRoot);
|
|
2747
2812
|
writeFileSync8(codebuddySettingsLocalPath(ctx.repoRoot), content, "utf8");
|
|
2748
2813
|
ctx.report.files.push({ path: CODEBUDDY_SETTINGS_LOCAL_REL, action });
|
|
@@ -2751,23 +2816,23 @@ var codebuddyAdapter = {
|
|
|
2751
2816
|
|
|
2752
2817
|
// src/init/assistants/cursor.ts
|
|
2753
2818
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
2754
|
-
import { join as
|
|
2819
|
+
import { join as join25 } from "path";
|
|
2755
2820
|
|
|
2756
2821
|
// src/init/mergeCursorHooks.ts
|
|
2757
|
-
import { existsSync as
|
|
2758
|
-
import { join as
|
|
2822
|
+
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "fs";
|
|
2823
|
+
import { join as join24 } from "path";
|
|
2759
2824
|
var CURSOR_HOOKS_REL = ".cursor/hooks.json";
|
|
2760
2825
|
function cursorHooksPath(repoRoot) {
|
|
2761
|
-
return
|
|
2826
|
+
return join24(repoRoot, ".cursor", "hooks.json");
|
|
2762
2827
|
}
|
|
2763
2828
|
function mergeCursorHooks(repoRoot) {
|
|
2764
2829
|
const hooksPath = cursorHooksPath(repoRoot);
|
|
2765
|
-
const existed =
|
|
2830
|
+
const existed = existsSync17(hooksPath);
|
|
2766
2831
|
const templateParsed = JSON.parse(renderTemplate("hooks.cursor.json.tpl"));
|
|
2767
2832
|
let existing = {};
|
|
2768
2833
|
if (existed) {
|
|
2769
2834
|
try {
|
|
2770
|
-
existing = JSON.parse(
|
|
2835
|
+
existing = JSON.parse(readFileSync18(hooksPath, "utf8"));
|
|
2771
2836
|
} catch {
|
|
2772
2837
|
existing = {};
|
|
2773
2838
|
}
|
|
@@ -2796,7 +2861,7 @@ var cursorAdapter = {
|
|
|
2796
2861
|
available: true,
|
|
2797
2862
|
scaffoldPaths: [CURSOR_HOOKS_REL],
|
|
2798
2863
|
write(ctx) {
|
|
2799
|
-
mkdirSync10(
|
|
2864
|
+
mkdirSync10(join25(ctx.repoRoot, ".cursor"), { recursive: true });
|
|
2800
2865
|
const { content, action } = mergeCursorHooks(ctx.repoRoot);
|
|
2801
2866
|
writeFileSync9(cursorHooksPath(ctx.repoRoot), content, "utf8");
|
|
2802
2867
|
ctx.report.files.push({ path: CURSOR_HOOKS_REL, action });
|
|
@@ -2859,29 +2924,29 @@ function validateAssistantSelection(ids) {
|
|
|
2859
2924
|
|
|
2860
2925
|
// src/init/ensureDirs.ts
|
|
2861
2926
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
2862
|
-
import { join as
|
|
2927
|
+
import { join as join26 } from "path";
|
|
2863
2928
|
function ensureMemoryTree(repoRoot) {
|
|
2864
|
-
const memoryRoot =
|
|
2929
|
+
const memoryRoot = join26(repoRoot, MEMORY_DIR);
|
|
2865
2930
|
mkdirSync11(memoryRoot, { recursive: true });
|
|
2866
2931
|
for (const sub of MEMORY_SUBDIRS) {
|
|
2867
|
-
mkdirSync11(
|
|
2932
|
+
mkdirSync11(join26(memoryRoot, sub), { recursive: true });
|
|
2868
2933
|
}
|
|
2869
|
-
mkdirSync11(
|
|
2934
|
+
mkdirSync11(join26(repoRoot, ".claude"), { recursive: true });
|
|
2870
2935
|
for (const sub of GITKEEP_DIRS) {
|
|
2871
|
-
const keepPath =
|
|
2936
|
+
const keepPath = join26(memoryRoot, sub, ".gitkeep");
|
|
2872
2937
|
writeFileSync10(keepPath, "", { flag: "a" });
|
|
2873
2938
|
}
|
|
2874
2939
|
}
|
|
2875
2940
|
|
|
2876
2941
|
// src/init/mergeAssistants.ts
|
|
2877
|
-
import { existsSync as
|
|
2942
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19 } from "fs";
|
|
2878
2943
|
function readExistingAssistants(repoRoot) {
|
|
2879
2944
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
2880
|
-
if (!
|
|
2945
|
+
if (!existsSync18(configPath)) {
|
|
2881
2946
|
return [];
|
|
2882
2947
|
}
|
|
2883
2948
|
try {
|
|
2884
|
-
const config = JSON.parse(
|
|
2949
|
+
const config = JSON.parse(readFileSync19(configPath, "utf8"));
|
|
2885
2950
|
if (!Array.isArray(config.assistants)) {
|
|
2886
2951
|
return [];
|
|
2887
2952
|
}
|
|
@@ -2896,16 +2961,16 @@ function mergeAssistants(repoRoot, selected) {
|
|
|
2896
2961
|
}
|
|
2897
2962
|
|
|
2898
2963
|
// src/init/mergeGitignore.ts
|
|
2899
|
-
import { existsSync as
|
|
2900
|
-
import { join as
|
|
2964
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
2965
|
+
import { join as join27 } from "path";
|
|
2901
2966
|
var START_MARKER = "# >>> hermes-repo memory (do not edit this block manually)";
|
|
2902
2967
|
var END_MARKER = "# <<< hermes-repo memory";
|
|
2903
2968
|
function mergeHermesGitignore(repoRoot) {
|
|
2904
2969
|
const block = readTemplate("gitignore-block.txt").trimEnd() + "\n";
|
|
2905
|
-
const gitignorePath =
|
|
2906
|
-
const contentBefore =
|
|
2970
|
+
const gitignorePath = join27(repoRoot, ".gitignore");
|
|
2971
|
+
const contentBefore = existsSync19(gitignorePath) ? readFileSync20(gitignorePath, "utf8") : "";
|
|
2907
2972
|
const warnBroadMemoryIgnore = contentBefore.length > 0 && !contentBefore.includes(START_MARKER) && /(^|\n)\.memory\/\s*$/m.test(contentBefore);
|
|
2908
|
-
if (!
|
|
2973
|
+
if (!existsSync19(gitignorePath)) {
|
|
2909
2974
|
writeFileSync11(gitignorePath, `${block}
|
|
2910
2975
|
`, "utf8");
|
|
2911
2976
|
return { action: "created", warnBroadMemoryIgnore: false };
|
|
@@ -2937,22 +3002,33 @@ function mergeHermesGitignore(repoRoot) {
|
|
|
2937
3002
|
import { copyFileSync, mkdirSync as mkdirSync12, writeFileSync as writeFileSync14 } from "fs";
|
|
2938
3003
|
|
|
2939
3004
|
// src/init/mergeConfig.ts
|
|
2940
|
-
import { existsSync as
|
|
3005
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "fs";
|
|
2941
3006
|
var DEFAULT_LLM = {
|
|
2942
3007
|
enabled: false,
|
|
3008
|
+
provider: "openai",
|
|
2943
3009
|
baseUrl: "https://api.openai.com/v1",
|
|
2944
|
-
model: "gpt-4o"
|
|
3010
|
+
model: "gpt-4o",
|
|
3011
|
+
apiKey: "",
|
|
3012
|
+
timeoutMs: 6e4,
|
|
3013
|
+
maxInputChars: 24e3,
|
|
3014
|
+
mode: "async"
|
|
2945
3015
|
};
|
|
2946
3016
|
var DEFAULT_CONSOLIDATE = {
|
|
2947
|
-
autoArchiveDays: 30
|
|
3017
|
+
autoArchiveDays: 30,
|
|
3018
|
+
autoFlush: {
|
|
3019
|
+
enabled: false,
|
|
3020
|
+
minPendingSessions: 3,
|
|
3021
|
+
minIntervalMinutes: 30,
|
|
3022
|
+
maxPendingChars: 2e4
|
|
3023
|
+
}
|
|
2948
3024
|
};
|
|
2949
3025
|
function mergeConfigForInit(repoRoot, assistants) {
|
|
2950
3026
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
2951
|
-
const existed =
|
|
3027
|
+
const existed = existsSync20(configPath);
|
|
2952
3028
|
let existing = {};
|
|
2953
3029
|
if (existed) {
|
|
2954
3030
|
try {
|
|
2955
|
-
existing = JSON.parse(
|
|
3031
|
+
existing = JSON.parse(readFileSync21(configPath, "utf8"));
|
|
2956
3032
|
} catch {
|
|
2957
3033
|
existing = {};
|
|
2958
3034
|
}
|
|
@@ -2960,6 +3036,7 @@ function mergeConfigForInit(repoRoot, assistants) {
|
|
|
2960
3036
|
const prevStorage = existing.storage && typeof existing.storage === "object" && !Array.isArray(existing.storage) ? existing.storage : {};
|
|
2961
3037
|
const prevLlm = existing.llm && typeof existing.llm === "object" && !Array.isArray(existing.llm) ? existing.llm : {};
|
|
2962
3038
|
const prevConsolidate = existing.consolidate && typeof existing.consolidate === "object" && !Array.isArray(existing.consolidate) ? existing.consolidate : {};
|
|
3039
|
+
const prevAutoFlush = prevConsolidate.autoFlush && typeof prevConsolidate.autoFlush === "object" && !Array.isArray(prevConsolidate.autoFlush) ? prevConsolidate.autoFlush : {};
|
|
2963
3040
|
const merged = {
|
|
2964
3041
|
...existing,
|
|
2965
3042
|
version: 2,
|
|
@@ -2970,7 +3047,14 @@ function mergeConfigForInit(repoRoot, assistants) {
|
|
|
2970
3047
|
assistants,
|
|
2971
3048
|
debug: existing.debug === true,
|
|
2972
3049
|
llm: { ...DEFAULT_LLM, ...prevLlm },
|
|
2973
|
-
consolidate: {
|
|
3050
|
+
consolidate: {
|
|
3051
|
+
...DEFAULT_CONSOLIDATE,
|
|
3052
|
+
...prevConsolidate,
|
|
3053
|
+
autoFlush: {
|
|
3054
|
+
...DEFAULT_CONSOLIDATE.autoFlush,
|
|
3055
|
+
...prevAutoFlush
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
2974
3058
|
};
|
|
2975
3059
|
return {
|
|
2976
3060
|
content: `${JSON.stringify(merged, null, 2)}
|
|
@@ -2980,8 +3064,8 @@ function mergeConfigForInit(repoRoot, assistants) {
|
|
|
2980
3064
|
}
|
|
2981
3065
|
|
|
2982
3066
|
// src/init/mergeAgentsMd.ts
|
|
2983
|
-
import { existsSync as
|
|
2984
|
-
import { join as
|
|
3067
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
|
|
3068
|
+
import { join as join28 } from "path";
|
|
2985
3069
|
var HERMES_AGENTS_START_MARKER = "<!-- >>> hermes-repo agents (do not edit this block manually) -->";
|
|
2986
3070
|
var HERMES_AGENTS_END_MARKER = "<!-- <<< hermes-repo agents -->";
|
|
2987
3071
|
function buildHermesAgentsBlockBody() {
|
|
@@ -3037,13 +3121,13 @@ function spliceHermesBlock2(existing, block) {
|
|
|
3037
3121
|
`;
|
|
3038
3122
|
}
|
|
3039
3123
|
function mergeAgentsMd(repoRoot, force) {
|
|
3040
|
-
const agentsPath =
|
|
3124
|
+
const agentsPath = join28(repoRoot, "AGENTS.md");
|
|
3041
3125
|
const block = buildHermesAgentsMarkedBlock();
|
|
3042
|
-
if (!
|
|
3126
|
+
if (!existsSync21(agentsPath)) {
|
|
3043
3127
|
writeFileSync12(agentsPath, buildNewAgentsMd(), "utf8");
|
|
3044
3128
|
return "created";
|
|
3045
3129
|
}
|
|
3046
|
-
const content =
|
|
3130
|
+
const content = readFileSync22(agentsPath, "utf8");
|
|
3047
3131
|
if (agentsMdHasHermesBlock(content)) {
|
|
3048
3132
|
if (!force) {
|
|
3049
3133
|
return "skipped";
|
|
@@ -3059,9 +3143,9 @@ function mergeAgentsMd(repoRoot, force) {
|
|
|
3059
3143
|
}
|
|
3060
3144
|
|
|
3061
3145
|
// src/init/scaffoldWrite.ts
|
|
3062
|
-
import { existsSync as
|
|
3146
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync13 } from "fs";
|
|
3063
3147
|
function shouldWriteFile(absolutePath, force) {
|
|
3064
|
-
if (!
|
|
3148
|
+
if (!existsSync22(absolutePath)) {
|
|
3065
3149
|
return { write: true, action: "created" };
|
|
3066
3150
|
}
|
|
3067
3151
|
if (force) {
|
|
@@ -3151,15 +3235,15 @@ function writeScaffoldFiles(repoRoot, opts, report) {
|
|
|
3151
3235
|
|
|
3152
3236
|
// src/init/prompts.ts
|
|
3153
3237
|
import { checkbox, confirm, input } from "@inquirer/prompts";
|
|
3154
|
-
import { existsSync as
|
|
3238
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23 } from "fs";
|
|
3155
3239
|
import { resolve as resolve6 } from "path";
|
|
3156
3240
|
function isInitialized(targetDir) {
|
|
3157
3241
|
const configPath = memoryPath(targetDir, "config.json");
|
|
3158
|
-
if (!
|
|
3242
|
+
if (!existsSync23(configPath)) {
|
|
3159
3243
|
return false;
|
|
3160
3244
|
}
|
|
3161
3245
|
try {
|
|
3162
|
-
const config = JSON.parse(
|
|
3246
|
+
const config = JSON.parse(readFileSync23(configPath, "utf8"));
|
|
3163
3247
|
return typeof config.version === "number" && config.version >= 1;
|
|
3164
3248
|
} catch {
|
|
3165
3249
|
return false;
|