@claude-sessions/core 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +70 -4
- package/dist/index.js +449 -32
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/{types-C2fzbmg9.d.ts → types-Cz8chaYQ.d.ts} +110 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var createLogger = (namespace) => ({
|
|
|
24
24
|
|
|
25
25
|
// src/paths.ts
|
|
26
26
|
var log = createLogger("paths");
|
|
27
|
-
var getSessionsDir = () => path.join(os.homedir(), ".claude", "projects");
|
|
27
|
+
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR || path.join(os.homedir(), ".claude", "projects");
|
|
28
28
|
var getTodosDir = () => path.join(os.homedir(), ".claude", "todos");
|
|
29
29
|
var extractCwdFromContent = (content) => {
|
|
30
30
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -80,22 +80,22 @@ var pathToFolderName = (absolutePath) => {
|
|
|
80
80
|
return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
|
|
81
81
|
};
|
|
82
82
|
var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
|
|
83
|
-
const
|
|
83
|
+
const basename3 = path.basename(filePath);
|
|
84
84
|
try {
|
|
85
85
|
const content = fileSystem.readFileSync(filePath, "utf-8");
|
|
86
86
|
const cwd = extractCwdFromContent(content);
|
|
87
87
|
if (cwd === null) {
|
|
88
88
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
89
89
|
if (lines.length === 0) {
|
|
90
|
-
logger2.debug(`tryGetCwdFromFile: ${
|
|
90
|
+
logger2.debug(`tryGetCwdFromFile: ${basename3} -> empty file`);
|
|
91
91
|
} else {
|
|
92
|
-
logger2.debug(`tryGetCwdFromFile: ${
|
|
92
|
+
logger2.debug(`tryGetCwdFromFile: ${basename3} -> no cwd found in ${lines.length} lines`);
|
|
93
93
|
}
|
|
94
94
|
return null;
|
|
95
95
|
}
|
|
96
96
|
return cwd;
|
|
97
97
|
} catch (e) {
|
|
98
|
-
logger2.warn(`tryGetCwdFromFile: ${
|
|
98
|
+
logger2.warn(`tryGetCwdFromFile: ${basename3} -> read error: ${e}`);
|
|
99
99
|
return null;
|
|
100
100
|
}
|
|
101
101
|
};
|
|
@@ -196,6 +196,17 @@ var isInvalidApiKeyMessage = (msg) => {
|
|
|
196
196
|
const text = extractTextContent(msg.message);
|
|
197
197
|
return text.includes("Invalid API key");
|
|
198
198
|
};
|
|
199
|
+
var ERROR_SESSION_PATTERNS = [
|
|
200
|
+
"API Error",
|
|
201
|
+
"authentication_error",
|
|
202
|
+
"Invalid API key",
|
|
203
|
+
"OAuth token has expired",
|
|
204
|
+
"Please run /login"
|
|
205
|
+
];
|
|
206
|
+
var isErrorSessionTitle = (title) => {
|
|
207
|
+
if (!title) return false;
|
|
208
|
+
return ERROR_SESSION_PATTERNS.some((pattern) => title.includes(pattern));
|
|
209
|
+
};
|
|
199
210
|
var isContinuationSummary = (msg) => {
|
|
200
211
|
if (msg.isCompactSummary === true) return true;
|
|
201
212
|
if (msg.type !== "user") return false;
|
|
@@ -287,46 +298,127 @@ var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
287
298
|
}
|
|
288
299
|
return linkedAgents;
|
|
289
300
|
});
|
|
290
|
-
var
|
|
301
|
+
var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
291
302
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
292
303
|
const files = yield* Effect.tryPromise(() => fs2.readdir(projectPath));
|
|
293
304
|
const sessionIds = new Set(
|
|
294
305
|
files.filter((f) => !f.startsWith("agent-") && f.endsWith(".jsonl")).map((f) => f.replace(".jsonl", ""))
|
|
295
306
|
);
|
|
296
|
-
const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
297
307
|
const orphanAgents = [];
|
|
298
|
-
|
|
308
|
+
const checkAgentFile = async (filePath) => {
|
|
309
|
+
try {
|
|
310
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
311
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
312
|
+
const firstLine = lines[0];
|
|
313
|
+
if (!firstLine) return null;
|
|
314
|
+
const parsed = JSON.parse(firstLine);
|
|
315
|
+
if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {
|
|
316
|
+
const fileName = path2.basename(filePath);
|
|
317
|
+
return {
|
|
318
|
+
agentId: fileName.replace(".jsonl", ""),
|
|
319
|
+
sessionId: parsed.sessionId,
|
|
320
|
+
lineCount: lines.length
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
};
|
|
327
|
+
const rootAgentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
328
|
+
for (const agentFile of rootAgentFiles) {
|
|
299
329
|
const filePath = path2.join(projectPath, agentFile);
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
330
|
+
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
331
|
+
if (orphan) {
|
|
332
|
+
orphanAgents.push({ ...orphan, filePath });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
for (const entry of files) {
|
|
336
|
+
const entryPath = path2.join(projectPath, entry);
|
|
337
|
+
const stat3 = yield* Effect.tryPromise(() => fs2.stat(entryPath).catch(() => null));
|
|
338
|
+
if (stat3?.isDirectory() && !entry.startsWith(".")) {
|
|
339
|
+
const subagentsPath = path2.join(entryPath, "subagents");
|
|
340
|
+
const subagentsExists = yield* Effect.tryPromise(
|
|
341
|
+
() => fs2.stat(subagentsPath).then(() => true).catch(() => false)
|
|
342
|
+
);
|
|
343
|
+
if (subagentsExists) {
|
|
344
|
+
const subagentFiles = yield* Effect.tryPromise(
|
|
345
|
+
() => fs2.readdir(subagentsPath).catch(() => [])
|
|
346
|
+
);
|
|
347
|
+
for (const subagentFile of subagentFiles) {
|
|
348
|
+
if (subagentFile.startsWith("agent-") && subagentFile.endsWith(".jsonl")) {
|
|
349
|
+
const filePath = path2.join(subagentsPath, subagentFile);
|
|
350
|
+
const orphan = yield* Effect.tryPromise(() => checkAgentFile(filePath));
|
|
351
|
+
if (orphan) {
|
|
352
|
+
orphanAgents.push({ ...orphan, filePath });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
310
355
|
}
|
|
311
|
-
} catch {
|
|
312
356
|
}
|
|
313
357
|
}
|
|
314
358
|
}
|
|
315
359
|
return orphanAgents;
|
|
316
360
|
});
|
|
361
|
+
var findOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
362
|
+
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
363
|
+
return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }));
|
|
364
|
+
});
|
|
317
365
|
var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
318
366
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
319
|
-
const orphans = yield*
|
|
320
|
-
const backupDir = path2.join(projectPath, ".bak");
|
|
321
|
-
yield* Effect.tryPromise(() => fs2.mkdir(backupDir, { recursive: true }));
|
|
367
|
+
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
322
368
|
const deletedAgents = [];
|
|
369
|
+
const backedUpAgents = [];
|
|
370
|
+
const cleanedFolders = [];
|
|
371
|
+
let backupDirCreated = false;
|
|
372
|
+
const foldersToCheck = /* @__PURE__ */ new Set();
|
|
323
373
|
for (const orphan of orphans) {
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
374
|
+
const parentDir = path2.dirname(orphan.filePath);
|
|
375
|
+
if (parentDir.endsWith("/subagents") || parentDir.endsWith("\\subagents")) {
|
|
376
|
+
foldersToCheck.add(parentDir);
|
|
377
|
+
}
|
|
378
|
+
if (orphan.lineCount <= 2) {
|
|
379
|
+
yield* Effect.tryPromise(() => fs2.unlink(orphan.filePath));
|
|
380
|
+
deletedAgents.push(orphan.agentId);
|
|
381
|
+
} else {
|
|
382
|
+
if (!backupDirCreated) {
|
|
383
|
+
const backupDir2 = path2.join(projectPath, ".bak");
|
|
384
|
+
yield* Effect.tryPromise(() => fs2.mkdir(backupDir2, { recursive: true }));
|
|
385
|
+
backupDirCreated = true;
|
|
386
|
+
}
|
|
387
|
+
const backupDir = path2.join(projectPath, ".bak");
|
|
388
|
+
const agentBackupPath = path2.join(backupDir, `${orphan.agentId}.jsonl`);
|
|
389
|
+
yield* Effect.tryPromise(() => fs2.rename(orphan.filePath, agentBackupPath));
|
|
390
|
+
backedUpAgents.push(orphan.agentId);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
for (const subagentsDir of foldersToCheck) {
|
|
394
|
+
const isEmpty = yield* Effect.tryPromise(async () => {
|
|
395
|
+
const files = await fs2.readdir(subagentsDir);
|
|
396
|
+
return files.length === 0;
|
|
397
|
+
});
|
|
398
|
+
if (isEmpty) {
|
|
399
|
+
yield* Effect.tryPromise(() => fs2.rmdir(subagentsDir));
|
|
400
|
+
cleanedFolders.push(subagentsDir);
|
|
401
|
+
const sessionDir = path2.dirname(subagentsDir);
|
|
402
|
+
const sessionDirEmpty = yield* Effect.tryPromise(async () => {
|
|
403
|
+
const files = await fs2.readdir(sessionDir);
|
|
404
|
+
return files.length === 0;
|
|
405
|
+
});
|
|
406
|
+
if (sessionDirEmpty) {
|
|
407
|
+
yield* Effect.tryPromise(() => fs2.rmdir(sessionDir));
|
|
408
|
+
cleanedFolders.push(sessionDir);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
328
411
|
}
|
|
329
|
-
return {
|
|
412
|
+
return {
|
|
413
|
+
success: true,
|
|
414
|
+
deletedAgents,
|
|
415
|
+
backedUpAgents,
|
|
416
|
+
cleanedFolders,
|
|
417
|
+
deletedCount: deletedAgents.length,
|
|
418
|
+
backedUpCount: backedUpAgents.length,
|
|
419
|
+
cleanedFolderCount: cleanedFolders.length,
|
|
420
|
+
count: deletedAgents.length + backedUpAgents.length
|
|
421
|
+
};
|
|
330
422
|
});
|
|
331
423
|
var loadAgentMessages = (projectName, _sessionId, agentId) => Effect.gen(function* () {
|
|
332
424
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
@@ -677,8 +769,8 @@ var deleteSession = (projectName, sessionId) => Effect3.gen(function* () {
|
|
|
677
769
|
const projectPath = path4.join(sessionsDir, projectName);
|
|
678
770
|
const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
|
|
679
771
|
const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
|
|
680
|
-
const
|
|
681
|
-
if (
|
|
772
|
+
const stat3 = yield* Effect3.tryPromise(() => fs4.stat(filePath));
|
|
773
|
+
if (stat3.size === 0) {
|
|
682
774
|
yield* Effect3.tryPromise(() => fs4.unlink(filePath));
|
|
683
775
|
const agentBackupDir2 = path4.join(projectPath, ".bak");
|
|
684
776
|
yield* Effect3.tryPromise(() => fs4.mkdir(agentBackupDir2, { recursive: true }));
|
|
@@ -854,6 +946,137 @@ var getSessionFiles = (projectName, sessionId) => Effect3.gen(function* () {
|
|
|
854
946
|
totalChanges: fileChanges.length
|
|
855
947
|
};
|
|
856
948
|
});
|
|
949
|
+
var analyzeSession = (projectName, sessionId) => Effect3.gen(function* () {
|
|
950
|
+
const messages = yield* readSession(projectName, sessionId);
|
|
951
|
+
let userMessages = 0;
|
|
952
|
+
let assistantMessages = 0;
|
|
953
|
+
let summaryCount = 0;
|
|
954
|
+
let snapshotCount = 0;
|
|
955
|
+
const toolUsageMap = /* @__PURE__ */ new Map();
|
|
956
|
+
const filesChanged = /* @__PURE__ */ new Set();
|
|
957
|
+
const patterns = [];
|
|
958
|
+
const milestones = [];
|
|
959
|
+
let firstTimestamp;
|
|
960
|
+
let lastTimestamp;
|
|
961
|
+
for (const msg of messages) {
|
|
962
|
+
if (msg.timestamp) {
|
|
963
|
+
if (!firstTimestamp) firstTimestamp = msg.timestamp;
|
|
964
|
+
lastTimestamp = msg.timestamp;
|
|
965
|
+
}
|
|
966
|
+
if (msg.type === "user") {
|
|
967
|
+
userMessages++;
|
|
968
|
+
const content = typeof msg.content === "string" ? msg.content : "";
|
|
969
|
+
if (content.toLowerCase().includes("commit") || content.toLowerCase().includes("\uC644\uB8CC")) {
|
|
970
|
+
milestones.push({
|
|
971
|
+
timestamp: msg.timestamp,
|
|
972
|
+
description: `User checkpoint: ${content.slice(0, 50)}...`,
|
|
973
|
+
messageUuid: msg.uuid
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
} else if (msg.type === "assistant") {
|
|
977
|
+
assistantMessages++;
|
|
978
|
+
if (msg.message?.content && Array.isArray(msg.message.content)) {
|
|
979
|
+
for (const item of msg.message.content) {
|
|
980
|
+
if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
|
|
981
|
+
const toolUse = item;
|
|
982
|
+
const toolName = toolUse.name ?? "unknown";
|
|
983
|
+
const existing = toolUsageMap.get(toolName) ?? { count: 0, errorCount: 0 };
|
|
984
|
+
existing.count++;
|
|
985
|
+
toolUsageMap.set(toolName, existing);
|
|
986
|
+
if ((toolName === "Write" || toolName === "Edit") && toolUse.input?.file_path) {
|
|
987
|
+
filesChanged.add(toolUse.input.file_path);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
} else if (msg.type === "summary") {
|
|
993
|
+
summaryCount++;
|
|
994
|
+
if (msg.summary) {
|
|
995
|
+
milestones.push({
|
|
996
|
+
timestamp: msg.timestamp,
|
|
997
|
+
description: `Summary: ${msg.summary.slice(0, 100)}...`,
|
|
998
|
+
messageUuid: msg.uuid
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
} else if (msg.type === "file-history-snapshot") {
|
|
1002
|
+
snapshotCount++;
|
|
1003
|
+
const snapshot = msg;
|
|
1004
|
+
if (snapshot.snapshot?.trackedFileBackups) {
|
|
1005
|
+
for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {
|
|
1006
|
+
filesChanged.add(filePath);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
for (const msg of messages) {
|
|
1012
|
+
if (msg.type === "user" && msg.content && Array.isArray(msg.content)) {
|
|
1013
|
+
for (const item of msg.content) {
|
|
1014
|
+
if (item && typeof item === "object" && "type" in item && item.type === "tool_result" && "is_error" in item && item.is_error) {
|
|
1015
|
+
const toolResultItem = item;
|
|
1016
|
+
const toolUseId = toolResultItem.tool_use_id;
|
|
1017
|
+
if (toolUseId) {
|
|
1018
|
+
for (const prevMsg of messages) {
|
|
1019
|
+
if (prevMsg.message?.content && Array.isArray(prevMsg.message.content)) {
|
|
1020
|
+
for (const prevItem of prevMsg.message.content) {
|
|
1021
|
+
if (prevItem && typeof prevItem === "object" && "type" in prevItem && prevItem.type === "tool_use" && "id" in prevItem && prevItem.id === toolUseId) {
|
|
1022
|
+
const toolName = prevItem.name ?? "unknown";
|
|
1023
|
+
const existing = toolUsageMap.get(toolName);
|
|
1024
|
+
if (existing) {
|
|
1025
|
+
existing.errorCount++;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
let durationMinutes = 0;
|
|
1037
|
+
if (firstTimestamp && lastTimestamp) {
|
|
1038
|
+
const first = new Date(firstTimestamp).getTime();
|
|
1039
|
+
const last = new Date(lastTimestamp).getTime();
|
|
1040
|
+
durationMinutes = Math.round((last - first) / 1e3 / 60);
|
|
1041
|
+
}
|
|
1042
|
+
const toolUsageArray = Array.from(toolUsageMap.entries()).map(([name, stats]) => ({
|
|
1043
|
+
name,
|
|
1044
|
+
count: stats.count,
|
|
1045
|
+
errorCount: stats.errorCount
|
|
1046
|
+
}));
|
|
1047
|
+
for (const tool of toolUsageArray) {
|
|
1048
|
+
if (tool.count >= 3 && tool.errorCount / tool.count > 0.3) {
|
|
1049
|
+
patterns.push({
|
|
1050
|
+
type: "high_error_rate",
|
|
1051
|
+
description: `${tool.name} had ${tool.errorCount}/${tool.count} errors (${Math.round(tool.errorCount / tool.count * 100)}%)`,
|
|
1052
|
+
count: tool.errorCount
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
if (snapshotCount > 10) {
|
|
1057
|
+
patterns.push({
|
|
1058
|
+
type: "many_snapshots",
|
|
1059
|
+
description: `${snapshotCount} file-history-snapshots could be compressed`,
|
|
1060
|
+
count: snapshotCount
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
return {
|
|
1064
|
+
sessionId,
|
|
1065
|
+
projectName,
|
|
1066
|
+
durationMinutes,
|
|
1067
|
+
stats: {
|
|
1068
|
+
totalMessages: messages.length,
|
|
1069
|
+
userMessages,
|
|
1070
|
+
assistantMessages,
|
|
1071
|
+
summaryCount,
|
|
1072
|
+
snapshotCount
|
|
1073
|
+
},
|
|
1074
|
+
toolUsage: toolUsageArray.sort((a, b) => b.count - a.count),
|
|
1075
|
+
filesChanged: Array.from(filesChanged),
|
|
1076
|
+
patterns,
|
|
1077
|
+
milestones
|
|
1078
|
+
};
|
|
1079
|
+
});
|
|
857
1080
|
var moveSession = (sourceProject, sessionId, targetProject) => Effect3.gen(function* () {
|
|
858
1081
|
const sessionsDir = getSessionsDir();
|
|
859
1082
|
const sourcePath = path4.join(sessionsDir, sourceProject);
|
|
@@ -1061,7 +1284,7 @@ var clearSessions = (options) => Effect3.gen(function* () {
|
|
|
1061
1284
|
clearEmpty = true,
|
|
1062
1285
|
clearInvalid = true,
|
|
1063
1286
|
skipWithTodos = true,
|
|
1064
|
-
clearOrphanAgents =
|
|
1287
|
+
clearOrphanAgents = true,
|
|
1065
1288
|
clearOrphanTodos = false
|
|
1066
1289
|
} = options;
|
|
1067
1290
|
const projects = yield* listProjects;
|
|
@@ -1385,12 +1608,18 @@ var loadProjectTreeData = (projectName) => Effect3.gen(function* () {
|
|
|
1385
1608
|
const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
1386
1609
|
return dateB - dateA;
|
|
1387
1610
|
});
|
|
1611
|
+
const filteredSessions = sortedSessions.filter((s) => {
|
|
1612
|
+
if (isErrorSessionTitle(s.title)) return false;
|
|
1613
|
+
if (isErrorSessionTitle(s.customTitle)) return false;
|
|
1614
|
+
if (isErrorSessionTitle(s.currentSummary)) return false;
|
|
1615
|
+
return true;
|
|
1616
|
+
});
|
|
1388
1617
|
return {
|
|
1389
1618
|
name: project.name,
|
|
1390
1619
|
displayName: project.displayName,
|
|
1391
1620
|
path: project.path,
|
|
1392
|
-
sessionCount:
|
|
1393
|
-
sessions:
|
|
1621
|
+
sessionCount: filteredSessions.length,
|
|
1622
|
+
sessions: filteredSessions
|
|
1394
1623
|
};
|
|
1395
1624
|
});
|
|
1396
1625
|
var updateSessionSummary = (projectName, sessionId, newSummary) => Effect3.gen(function* () {
|
|
@@ -1414,8 +1643,194 @@ var updateSessionSummary = (projectName, sessionId, newSummary) => Effect3.gen(f
|
|
|
1414
1643
|
yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
|
|
1415
1644
|
return { success: true };
|
|
1416
1645
|
});
|
|
1646
|
+
var compressSession = (projectName, sessionId, options = {}) => Effect3.gen(function* () {
|
|
1647
|
+
const { keepSnapshots = "first_last", maxToolOutputLength = 5e3 } = options;
|
|
1648
|
+
const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
1649
|
+
const content = yield* Effect3.tryPromise(() => fs4.readFile(filePath, "utf-8"));
|
|
1650
|
+
const originalSize = Buffer.byteLength(content, "utf-8");
|
|
1651
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
1652
|
+
const messages = lines.map((line) => JSON.parse(line));
|
|
1653
|
+
let removedSnapshots = 0;
|
|
1654
|
+
let truncatedOutputs = 0;
|
|
1655
|
+
const snapshotIndices = [];
|
|
1656
|
+
messages.forEach((msg, idx) => {
|
|
1657
|
+
if (msg.type === "file-history-snapshot") {
|
|
1658
|
+
snapshotIndices.push(idx);
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
const filteredMessages = messages.filter((msg, idx) => {
|
|
1662
|
+
if (msg.type === "file-history-snapshot") {
|
|
1663
|
+
if (keepSnapshots === "none") {
|
|
1664
|
+
removedSnapshots++;
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
if (keepSnapshots === "first_last") {
|
|
1668
|
+
const isFirst = idx === snapshotIndices[0];
|
|
1669
|
+
const isLast = idx === snapshotIndices[snapshotIndices.length - 1];
|
|
1670
|
+
if (!isFirst && !isLast) {
|
|
1671
|
+
removedSnapshots++;
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return true;
|
|
1677
|
+
});
|
|
1678
|
+
for (const msg of filteredMessages) {
|
|
1679
|
+
if (msg.type === "user" && Array.isArray(msg.content)) {
|
|
1680
|
+
for (const item of msg.content) {
|
|
1681
|
+
if (item.type === "tool_result" && typeof item.content === "string") {
|
|
1682
|
+
if (maxToolOutputLength > 0 && item.content.length > maxToolOutputLength) {
|
|
1683
|
+
item.content = item.content.slice(0, maxToolOutputLength) + "\n... [truncated]";
|
|
1684
|
+
truncatedOutputs++;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
const newContent = filteredMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1691
|
+
const compressedSize = Buffer.byteLength(newContent, "utf-8");
|
|
1692
|
+
yield* Effect3.tryPromise(() => fs4.writeFile(filePath, newContent, "utf-8"));
|
|
1693
|
+
return {
|
|
1694
|
+
success: true,
|
|
1695
|
+
originalSize,
|
|
1696
|
+
compressedSize,
|
|
1697
|
+
removedSnapshots,
|
|
1698
|
+
truncatedOutputs
|
|
1699
|
+
};
|
|
1700
|
+
});
|
|
1701
|
+
var extractProjectKnowledge = (projectName, sessionIds) => Effect3.gen(function* () {
|
|
1702
|
+
const sessionsDir = getSessionsDir();
|
|
1703
|
+
const projectDir = path4.join(sessionsDir, projectName);
|
|
1704
|
+
let targetSessionIds = sessionIds;
|
|
1705
|
+
if (!targetSessionIds) {
|
|
1706
|
+
const files = yield* Effect3.tryPromise(() => fs4.readdir(projectDir));
|
|
1707
|
+
targetSessionIds = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).map((f) => f.replace(".jsonl", ""));
|
|
1708
|
+
}
|
|
1709
|
+
const fileModifyCount = /* @__PURE__ */ new Map();
|
|
1710
|
+
const toolSequences = [];
|
|
1711
|
+
const decisions = [];
|
|
1712
|
+
for (const sessionId of targetSessionIds) {
|
|
1713
|
+
try {
|
|
1714
|
+
const messages = yield* readSession(projectName, sessionId);
|
|
1715
|
+
for (const msg of messages) {
|
|
1716
|
+
if (msg.type === "file-history-snapshot") {
|
|
1717
|
+
const snapshot = msg;
|
|
1718
|
+
if (snapshot.snapshot?.trackedFileBackups) {
|
|
1719
|
+
for (const filePath of Object.keys(snapshot.snapshot.trackedFileBackups)) {
|
|
1720
|
+
const existing = fileModifyCount.get(filePath) ?? { count: 0 };
|
|
1721
|
+
existing.count++;
|
|
1722
|
+
existing.lastModified = snapshot.snapshot.timestamp;
|
|
1723
|
+
fileModifyCount.set(filePath, existing);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (msg.type === "assistant" && msg.message?.content && Array.isArray(msg.message.content)) {
|
|
1728
|
+
const tools = [];
|
|
1729
|
+
for (const item of msg.message.content) {
|
|
1730
|
+
if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
|
|
1731
|
+
const toolUse = item;
|
|
1732
|
+
if (toolUse.name) tools.push(toolUse.name);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (tools.length > 1) {
|
|
1736
|
+
toolSequences.push(tools);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (msg.type === "summary" && msg.summary) {
|
|
1740
|
+
decisions.push({
|
|
1741
|
+
context: "Session summary",
|
|
1742
|
+
decision: msg.summary.slice(0, 200),
|
|
1743
|
+
sessionId
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
} catch {
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const hotFiles = Array.from(fileModifyCount.entries()).map(([filePath, data]) => ({
|
|
1752
|
+
path: filePath,
|
|
1753
|
+
modifyCount: data.count,
|
|
1754
|
+
lastModified: data.lastModified
|
|
1755
|
+
})).sort((a, b) => b.modifyCount - a.modifyCount).slice(0, 20);
|
|
1756
|
+
const workflowMap = /* @__PURE__ */ new Map();
|
|
1757
|
+
for (const seq of toolSequences) {
|
|
1758
|
+
const key = seq.join(" -> ");
|
|
1759
|
+
workflowMap.set(key, (workflowMap.get(key) ?? 0) + 1);
|
|
1760
|
+
}
|
|
1761
|
+
const workflows = Array.from(workflowMap.entries()).filter(([, count]) => count >= 2).map(([sequence, count]) => ({
|
|
1762
|
+
sequence: sequence.split(" -> "),
|
|
1763
|
+
count
|
|
1764
|
+
})).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
1765
|
+
return {
|
|
1766
|
+
projectName,
|
|
1767
|
+
patterns: [],
|
|
1768
|
+
hotFiles,
|
|
1769
|
+
workflows,
|
|
1770
|
+
decisions: decisions.slice(0, 20)
|
|
1771
|
+
};
|
|
1772
|
+
});
|
|
1773
|
+
var summarizeSession = (projectName, sessionId, options = {}) => Effect3.gen(function* () {
|
|
1774
|
+
const { limit = 50, maxLength = 100 } = options;
|
|
1775
|
+
const messages = yield* readSession(projectName, sessionId);
|
|
1776
|
+
const lines = [];
|
|
1777
|
+
let count = 0;
|
|
1778
|
+
for (const msg of messages) {
|
|
1779
|
+
if (count >= limit) break;
|
|
1780
|
+
if (msg.type === "user" || msg.type === "human") {
|
|
1781
|
+
let timeStr;
|
|
1782
|
+
if (msg.timestamp) {
|
|
1783
|
+
try {
|
|
1784
|
+
const dt = new Date(msg.timestamp);
|
|
1785
|
+
timeStr = dt.toLocaleString("ko-KR", {
|
|
1786
|
+
month: "2-digit",
|
|
1787
|
+
day: "2-digit",
|
|
1788
|
+
hour: "2-digit",
|
|
1789
|
+
minute: "2-digit",
|
|
1790
|
+
hour12: false
|
|
1791
|
+
});
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
const text = extractTextContent(msg.message);
|
|
1796
|
+
if (text) {
|
|
1797
|
+
const truncated = truncateText(text, maxLength);
|
|
1798
|
+
lines.push({ role: "user", content: truncated, timestamp: timeStr });
|
|
1799
|
+
count++;
|
|
1800
|
+
}
|
|
1801
|
+
} else if (msg.type === "assistant") {
|
|
1802
|
+
const text = extractTextContent(msg.message);
|
|
1803
|
+
if (text) {
|
|
1804
|
+
const truncated = truncateText(text, maxLength);
|
|
1805
|
+
lines.push({ role: "assistant", content: truncated });
|
|
1806
|
+
count++;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
const formatted = lines.map((line) => {
|
|
1811
|
+
if (line.role === "user") {
|
|
1812
|
+
return line.timestamp ? `user [${line.timestamp}]: ${line.content}` : `user: ${line.content}`;
|
|
1813
|
+
}
|
|
1814
|
+
return `assistant: ${line.content}`;
|
|
1815
|
+
}).join("\n");
|
|
1816
|
+
return {
|
|
1817
|
+
sessionId,
|
|
1818
|
+
projectName,
|
|
1819
|
+
lines,
|
|
1820
|
+
formatted
|
|
1821
|
+
};
|
|
1822
|
+
});
|
|
1823
|
+
function truncateText(text, maxLen) {
|
|
1824
|
+
const cleaned = text.replace(/\n/g, " ");
|
|
1825
|
+
if (cleaned.length > maxLen) {
|
|
1826
|
+
return cleaned.slice(0, maxLen) + "...";
|
|
1827
|
+
}
|
|
1828
|
+
return cleaned;
|
|
1829
|
+
}
|
|
1417
1830
|
export {
|
|
1831
|
+
analyzeSession,
|
|
1418
1832
|
clearSessions,
|
|
1833
|
+
compressSession,
|
|
1419
1834
|
createLogger,
|
|
1420
1835
|
deleteLinkedTodos,
|
|
1421
1836
|
deleteMessage,
|
|
@@ -1423,6 +1838,7 @@ export {
|
|
|
1423
1838
|
deleteOrphanTodos,
|
|
1424
1839
|
deleteSession,
|
|
1425
1840
|
displayPathToFolderName,
|
|
1841
|
+
extractProjectKnowledge,
|
|
1426
1842
|
extractTextContent,
|
|
1427
1843
|
extractTitle,
|
|
1428
1844
|
findLinkedAgents,
|
|
@@ -1457,6 +1873,7 @@ export {
|
|
|
1457
1873
|
setLogger,
|
|
1458
1874
|
sortProjects,
|
|
1459
1875
|
splitSession,
|
|
1876
|
+
summarizeSession,
|
|
1460
1877
|
updateSessionSummary
|
|
1461
1878
|
};
|
|
1462
1879
|
//# sourceMappingURL=index.js.map
|