@claude-sessions/core 0.4.1 → 0.4.2-beta.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/dist/index.d.ts +40 -18
- package/dist/index.js +465 -431
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/{types-DZsLFIFK.d.ts → types-mWa378iC.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/paths.ts
|
|
2
|
-
import * as
|
|
2
|
+
import * as fs2 from "fs";
|
|
3
3
|
import * as os from "os";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
|
|
@@ -22,19 +22,153 @@ var createLogger = (namespace) => ({
|
|
|
22
22
|
error: (msg, ...args) => currentLogger.error(`[${namespace}] ${msg}`, ...args)
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
// src/utils.ts
|
|
26
|
+
import { Effect } from "effect";
|
|
27
|
+
import * as fs from "fs/promises";
|
|
28
|
+
var logger = createLogger("utils");
|
|
29
|
+
var extractTextContent = (message) => {
|
|
30
|
+
if (!message) return "";
|
|
31
|
+
const content = message.content;
|
|
32
|
+
if (!content) return "";
|
|
33
|
+
if (typeof content === "string") return content;
|
|
34
|
+
if (Array.isArray(content)) {
|
|
35
|
+
return content.filter((item) => typeof item === "object" && item?.type === "text").map((item) => {
|
|
36
|
+
if (item.text == null) {
|
|
37
|
+
logger.warn("TextContent item has undefined or null text property");
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
40
|
+
return item.text;
|
|
41
|
+
}).join("");
|
|
42
|
+
}
|
|
43
|
+
return "";
|
|
44
|
+
};
|
|
45
|
+
var parseCommandMessage = (content) => {
|
|
46
|
+
const name = content?.match(/<command-name>([^<]+)<\/command-name>/)?.[1] ?? "";
|
|
47
|
+
const message = content?.match(/<command-message>([^<]+)<\/command-message>/)?.[1] ?? "";
|
|
48
|
+
return { name, message };
|
|
49
|
+
};
|
|
50
|
+
var extractTitle = (text) => {
|
|
51
|
+
if (!text) return "Untitled";
|
|
52
|
+
const { name } = parseCommandMessage(text);
|
|
53
|
+
if (name) return name;
|
|
54
|
+
let cleaned = text.replace(/<ide_[^>]*>[\s\S]*?<\/ide_[^>]*>/g, "").trim();
|
|
55
|
+
if (!cleaned) return "Untitled";
|
|
56
|
+
if (cleaned.includes("\n\n")) {
|
|
57
|
+
cleaned = cleaned.split("\n\n")[0];
|
|
58
|
+
}
|
|
59
|
+
if (cleaned.length > 100) {
|
|
60
|
+
return cleaned.slice(0, 100) + "...";
|
|
61
|
+
}
|
|
62
|
+
return cleaned || "Untitled";
|
|
63
|
+
};
|
|
64
|
+
var isInvalidApiKeyMessage = (msg) => {
|
|
65
|
+
const text = extractTextContent(msg.message);
|
|
66
|
+
return text.includes("Invalid API key");
|
|
67
|
+
};
|
|
68
|
+
var ERROR_SESSION_PATTERNS = [
|
|
69
|
+
"API Error",
|
|
70
|
+
"authentication_error",
|
|
71
|
+
"Invalid API key",
|
|
72
|
+
"OAuth token has expired",
|
|
73
|
+
"Please run /login"
|
|
74
|
+
];
|
|
75
|
+
var isErrorSessionTitle = (title) => {
|
|
76
|
+
if (!title) return false;
|
|
77
|
+
return ERROR_SESSION_PATTERNS.some((pattern) => title.includes(pattern));
|
|
78
|
+
};
|
|
79
|
+
var isContinuationSummary = (msg) => {
|
|
80
|
+
if (msg.isCompactSummary === true) return true;
|
|
81
|
+
if (msg.type !== "user") return false;
|
|
82
|
+
const text = extractTextContent(msg.message);
|
|
83
|
+
return text.startsWith("This session is being continued from");
|
|
84
|
+
};
|
|
85
|
+
var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallback = "Untitled") => {
|
|
86
|
+
if (customTitle) return customTitle;
|
|
87
|
+
if (currentSummary) {
|
|
88
|
+
return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
|
|
89
|
+
}
|
|
90
|
+
if (title && title !== "Untitled") {
|
|
91
|
+
if (title.includes("<command-name>")) {
|
|
92
|
+
const { name } = parseCommandMessage(title);
|
|
93
|
+
if (name) return name;
|
|
94
|
+
}
|
|
95
|
+
return title;
|
|
96
|
+
}
|
|
97
|
+
return fallback;
|
|
98
|
+
};
|
|
99
|
+
var replaceMessageContent = (msg, text) => ({
|
|
100
|
+
...msg,
|
|
101
|
+
message: {
|
|
102
|
+
...msg.message,
|
|
103
|
+
content: [{ type: "text", text }]
|
|
104
|
+
},
|
|
105
|
+
toolUseResult: void 0
|
|
106
|
+
});
|
|
107
|
+
var cleanupSplitFirstMessage = (msg) => {
|
|
108
|
+
const toolUseResult = msg.toolUseResult;
|
|
109
|
+
if (!toolUseResult) return msg;
|
|
110
|
+
if (typeof toolUseResult === "object" && "answers" in toolUseResult) {
|
|
111
|
+
const answers = toolUseResult.answers;
|
|
112
|
+
const qaText = Object.entries(answers).map(([q, a]) => `Q: ${q}
|
|
113
|
+
A: ${a}`).join("\n\n");
|
|
114
|
+
return replaceMessageContent(msg, qaText);
|
|
115
|
+
}
|
|
116
|
+
if (typeof toolUseResult === "string") {
|
|
117
|
+
const rejectionMarker = "The user provided the following reason for the rejection:";
|
|
118
|
+
const rejectionIndex = toolUseResult.indexOf(rejectionMarker);
|
|
119
|
+
if (rejectionIndex === -1) return msg;
|
|
120
|
+
const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim();
|
|
121
|
+
if (!text) return msg;
|
|
122
|
+
return replaceMessageContent(msg, text);
|
|
123
|
+
}
|
|
124
|
+
return msg;
|
|
125
|
+
};
|
|
126
|
+
var maskHomePath = (text, homeDir) => {
|
|
127
|
+
if (!homeDir) return text;
|
|
128
|
+
const escapedHome = homeDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
129
|
+
const regex = new RegExp(`${escapedHome}(?=[/\\\\]|$)`, "g");
|
|
130
|
+
return text.replace(regex, "~");
|
|
131
|
+
};
|
|
132
|
+
var getSessionSortTimestamp = (session) => {
|
|
133
|
+
const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt;
|
|
134
|
+
return timestampStr ? new Date(timestampStr).getTime() : 0;
|
|
135
|
+
};
|
|
136
|
+
var tryParseJsonLine = (line, lineNumber, filePath) => {
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(line);
|
|
139
|
+
} catch {
|
|
140
|
+
if (filePath) {
|
|
141
|
+
console.warn(`Skipping invalid JSON at line ${lineNumber} in ${filePath}`);
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
var parseJsonlLines = (lines, filePath) => {
|
|
147
|
+
return lines.map((line, idx) => {
|
|
148
|
+
try {
|
|
149
|
+
return JSON.parse(line);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
const err = e;
|
|
152
|
+
throw new Error(`Failed to parse line ${idx + 1} in ${filePath}: ${err.message}`);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
var readJsonlFile = (filePath) => Effect.gen(function* () {
|
|
157
|
+
const content = yield* Effect.tryPromise(() => fs.readFile(filePath, "utf-8"));
|
|
158
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
159
|
+
return parseJsonlLines(lines, filePath);
|
|
160
|
+
});
|
|
161
|
+
|
|
25
162
|
// src/paths.ts
|
|
26
163
|
var log = createLogger("paths");
|
|
27
164
|
var getSessionsDir = () => process.env.CLAUDE_SESSIONS_DIR || path.join(os.homedir(), ".claude", "projects");
|
|
28
165
|
var getTodosDir = () => path.join(os.homedir(), ".claude", "todos");
|
|
29
|
-
var extractCwdFromContent = (content) => {
|
|
166
|
+
var extractCwdFromContent = (content, filePath) => {
|
|
30
167
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
31
|
-
for (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return parsed.cwd;
|
|
36
|
-
}
|
|
37
|
-
} catch {
|
|
168
|
+
for (let i = 0; i < lines.length; i++) {
|
|
169
|
+
const parsed = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
170
|
+
if (parsed?.cwd) {
|
|
171
|
+
return parsed.cwd;
|
|
38
172
|
}
|
|
39
173
|
}
|
|
40
174
|
return null;
|
|
@@ -79,7 +213,7 @@ var pathToFolderName = (absolutePath) => {
|
|
|
79
213
|
}
|
|
80
214
|
return convertNonAscii(absolutePath).replace(/^\//g, "-").replace(/\/\./g, "--").replace(/\//g, "-").replace(/\./g, "-");
|
|
81
215
|
};
|
|
82
|
-
var tryGetCwdFromFile = (filePath, fileSystem =
|
|
216
|
+
var tryGetCwdFromFile = (filePath, fileSystem = fs2, logger2 = log) => {
|
|
83
217
|
const basename3 = path.basename(filePath);
|
|
84
218
|
try {
|
|
85
219
|
const content = fileSystem.readFileSync(filePath, "utf-8");
|
|
@@ -99,7 +233,7 @@ var tryGetCwdFromFile = (filePath, fileSystem = fs, logger2 = log) => {
|
|
|
99
233
|
return null;
|
|
100
234
|
}
|
|
101
235
|
};
|
|
102
|
-
var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem =
|
|
236
|
+
var getRealPathFromSession = (folderName, sessionsDir = getSessionsDir(), fileSystem = fs2, logger2 = log) => {
|
|
103
237
|
const projectDir = path.join(sessionsDir, folderName);
|
|
104
238
|
try {
|
|
105
239
|
const files = fileSystem.readdirSync(projectDir).filter(isSessionFile);
|
|
@@ -135,7 +269,7 @@ var folderNameToPath = (folderName) => {
|
|
|
135
269
|
const absolutePath = folderNameToDisplayPath(folderName);
|
|
136
270
|
return toRelativePath(absolutePath, homeDir);
|
|
137
271
|
};
|
|
138
|
-
var findProjectByWorkspacePath = (workspacePath, projectNames, sessionsDir = getSessionsDir(), fileSystem =
|
|
272
|
+
var findProjectByWorkspacePath = (workspacePath, projectNames, sessionsDir = getSessionsDir(), fileSystem = fs2, logger2 = log) => {
|
|
139
273
|
const directMatch = pathToFolderName(workspacePath);
|
|
140
274
|
if (projectNames.includes(directMatch)) {
|
|
141
275
|
return directMatch;
|
|
@@ -160,111 +294,7 @@ var findProjectByWorkspacePath = (workspacePath, projectNames, sessionsDir = get
|
|
|
160
294
|
return null;
|
|
161
295
|
};
|
|
162
296
|
|
|
163
|
-
// src/
|
|
164
|
-
var logger = createLogger("utils");
|
|
165
|
-
var extractTextContent = (message) => {
|
|
166
|
-
if (!message) return "";
|
|
167
|
-
const content = message.content;
|
|
168
|
-
if (!content) return "";
|
|
169
|
-
if (typeof content === "string") return content;
|
|
170
|
-
if (Array.isArray(content)) {
|
|
171
|
-
return content.filter((item) => typeof item === "object" && item?.type === "text").map((item) => {
|
|
172
|
-
if (item.text == null) {
|
|
173
|
-
logger.warn("TextContent item has undefined or null text property");
|
|
174
|
-
return "";
|
|
175
|
-
}
|
|
176
|
-
return item.text;
|
|
177
|
-
}).join("");
|
|
178
|
-
}
|
|
179
|
-
return "";
|
|
180
|
-
};
|
|
181
|
-
var parseCommandMessage = (content) => {
|
|
182
|
-
const name = content?.match(/<command-name>([^<]+)<\/command-name>/)?.[1] ?? "";
|
|
183
|
-
const message = content?.match(/<command-message>([^<]+)<\/command-message>/)?.[1] ?? "";
|
|
184
|
-
return { name, message };
|
|
185
|
-
};
|
|
186
|
-
var extractTitle = (text) => {
|
|
187
|
-
if (!text) return "Untitled";
|
|
188
|
-
const { name } = parseCommandMessage(text);
|
|
189
|
-
if (name) return name;
|
|
190
|
-
let cleaned = text.replace(/<ide_[^>]*>[\s\S]*?<\/ide_[^>]*>/g, "").trim();
|
|
191
|
-
if (!cleaned) return "Untitled";
|
|
192
|
-
if (cleaned.includes("\n\n")) {
|
|
193
|
-
cleaned = cleaned.split("\n\n")[0];
|
|
194
|
-
}
|
|
195
|
-
if (cleaned.length > 100) {
|
|
196
|
-
return cleaned.slice(0, 100) + "...";
|
|
197
|
-
}
|
|
198
|
-
return cleaned || "Untitled";
|
|
199
|
-
};
|
|
200
|
-
var isInvalidApiKeyMessage = (msg) => {
|
|
201
|
-
const text = extractTextContent(msg.message);
|
|
202
|
-
return text.includes("Invalid API key");
|
|
203
|
-
};
|
|
204
|
-
var ERROR_SESSION_PATTERNS = [
|
|
205
|
-
"API Error",
|
|
206
|
-
"authentication_error",
|
|
207
|
-
"Invalid API key",
|
|
208
|
-
"OAuth token has expired",
|
|
209
|
-
"Please run /login"
|
|
210
|
-
];
|
|
211
|
-
var isErrorSessionTitle = (title) => {
|
|
212
|
-
if (!title) return false;
|
|
213
|
-
return ERROR_SESSION_PATTERNS.some((pattern) => title.includes(pattern));
|
|
214
|
-
};
|
|
215
|
-
var isContinuationSummary = (msg) => {
|
|
216
|
-
if (msg.isCompactSummary === true) return true;
|
|
217
|
-
if (msg.type !== "user") return false;
|
|
218
|
-
const text = extractTextContent(msg.message);
|
|
219
|
-
return text.startsWith("This session is being continued from");
|
|
220
|
-
};
|
|
221
|
-
var getDisplayTitle = (customTitle, currentSummary, title, maxLength = 60, fallback = "Untitled") => {
|
|
222
|
-
if (customTitle) return customTitle;
|
|
223
|
-
if (currentSummary) {
|
|
224
|
-
return currentSummary.length > maxLength ? currentSummary.slice(0, maxLength - 3) + "..." : currentSummary;
|
|
225
|
-
}
|
|
226
|
-
if (title && title !== "Untitled") {
|
|
227
|
-
if (title.includes("<command-name>")) {
|
|
228
|
-
const { name } = parseCommandMessage(title);
|
|
229
|
-
if (name) return name;
|
|
230
|
-
}
|
|
231
|
-
return title;
|
|
232
|
-
}
|
|
233
|
-
return fallback;
|
|
234
|
-
};
|
|
235
|
-
var replaceMessageContent = (msg, text) => ({
|
|
236
|
-
...msg,
|
|
237
|
-
message: {
|
|
238
|
-
...msg.message,
|
|
239
|
-
content: [{ type: "text", text }]
|
|
240
|
-
},
|
|
241
|
-
toolUseResult: void 0
|
|
242
|
-
});
|
|
243
|
-
var cleanupSplitFirstMessage = (msg) => {
|
|
244
|
-
const toolUseResult = msg.toolUseResult;
|
|
245
|
-
if (!toolUseResult) return msg;
|
|
246
|
-
if (typeof toolUseResult === "object" && "answers" in toolUseResult) {
|
|
247
|
-
const answers = toolUseResult.answers;
|
|
248
|
-
const qaText = Object.entries(answers).map(([q, a]) => `Q: ${q}
|
|
249
|
-
A: ${a}`).join("\n\n");
|
|
250
|
-
return replaceMessageContent(msg, qaText);
|
|
251
|
-
}
|
|
252
|
-
if (typeof toolUseResult === "string") {
|
|
253
|
-
const rejectionMarker = "The user provided the following reason for the rejection:";
|
|
254
|
-
const rejectionIndex = toolUseResult.indexOf(rejectionMarker);
|
|
255
|
-
if (rejectionIndex === -1) return msg;
|
|
256
|
-
const text = toolUseResult.slice(rejectionIndex + rejectionMarker.length).trim();
|
|
257
|
-
if (!text) return msg;
|
|
258
|
-
return replaceMessageContent(msg, text);
|
|
259
|
-
}
|
|
260
|
-
return msg;
|
|
261
|
-
};
|
|
262
|
-
var maskHomePath = (text, homeDir) => {
|
|
263
|
-
if (!homeDir) return text;
|
|
264
|
-
const escapedHome = homeDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
265
|
-
const regex = new RegExp(`${escapedHome}(?=[/\\\\]|$)`, "g");
|
|
266
|
-
return text.replace(regex, "~");
|
|
267
|
-
};
|
|
297
|
+
// src/projects.ts
|
|
268
298
|
var sortProjects = (projects, options = {}) => {
|
|
269
299
|
const { currentProjectName, homeDir, filterEmpty = true } = options;
|
|
270
300
|
const filtered = filterEmpty ? projects.filter((p) => p.sessionCount > 0) : projects;
|
|
@@ -283,23 +313,19 @@ var sortProjects = (projects, options = {}) => {
|
|
|
283
313
|
return a.displayName.localeCompare(b.displayName);
|
|
284
314
|
});
|
|
285
315
|
};
|
|
286
|
-
var getSessionSortTimestamp = (session) => {
|
|
287
|
-
const timestampStr = session.summaries?.[0]?.timestamp ?? session.createdAt;
|
|
288
|
-
return timestampStr ? new Date(timestampStr).getTime() : 0;
|
|
289
|
-
};
|
|
290
316
|
|
|
291
317
|
// src/agents.ts
|
|
292
|
-
import { Effect } from "effect";
|
|
293
|
-
import * as
|
|
318
|
+
import { Effect as Effect2 } from "effect";
|
|
319
|
+
import * as fs3 from "fs/promises";
|
|
294
320
|
import * as path2 from "path";
|
|
295
|
-
var findLinkedAgents = (projectName, sessionId) =>
|
|
321
|
+
var findLinkedAgents = (projectName, sessionId) => Effect2.gen(function* () {
|
|
296
322
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
297
|
-
const files = yield*
|
|
323
|
+
const files = yield* Effect2.tryPromise(() => fs3.readdir(projectPath));
|
|
298
324
|
const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
299
325
|
const linkedAgents = [];
|
|
300
326
|
for (const agentFile of agentFiles) {
|
|
301
327
|
const filePath = path2.join(projectPath, agentFile);
|
|
302
|
-
const content = yield*
|
|
328
|
+
const content = yield* Effect2.tryPromise(() => fs3.readFile(filePath, "utf-8"));
|
|
303
329
|
const firstLine = content.split("\n")[0];
|
|
304
330
|
if (firstLine) {
|
|
305
331
|
try {
|
|
@@ -313,16 +339,16 @@ var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
|
|
|
313
339
|
}
|
|
314
340
|
return linkedAgents;
|
|
315
341
|
});
|
|
316
|
-
var findOrphanAgentsWithPaths = (projectName) =>
|
|
342
|
+
var findOrphanAgentsWithPaths = (projectName) => Effect2.gen(function* () {
|
|
317
343
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
318
|
-
const files = yield*
|
|
344
|
+
const files = yield* Effect2.tryPromise(() => fs3.readdir(projectPath));
|
|
319
345
|
const sessionIds = new Set(
|
|
320
346
|
files.filter((f) => !f.startsWith("agent-") && f.endsWith(".jsonl")).map((f) => f.replace(".jsonl", ""))
|
|
321
347
|
);
|
|
322
348
|
const orphanAgents = [];
|
|
323
349
|
const checkAgentFile = async (filePath) => {
|
|
324
350
|
try {
|
|
325
|
-
const content = await
|
|
351
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
326
352
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
327
353
|
const firstLine = lines[0];
|
|
328
354
|
if (!firstLine) return null;
|
|
@@ -342,27 +368,27 @@ var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
|
342
368
|
const rootAgentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
343
369
|
for (const agentFile of rootAgentFiles) {
|
|
344
370
|
const filePath = path2.join(projectPath, agentFile);
|
|
345
|
-
const orphan = yield*
|
|
371
|
+
const orphan = yield* Effect2.tryPromise(() => checkAgentFile(filePath));
|
|
346
372
|
if (orphan) {
|
|
347
373
|
orphanAgents.push({ ...orphan, filePath });
|
|
348
374
|
}
|
|
349
375
|
}
|
|
350
376
|
for (const entry of files) {
|
|
351
377
|
const entryPath = path2.join(projectPath, entry);
|
|
352
|
-
const stat4 = yield*
|
|
378
|
+
const stat4 = yield* Effect2.tryPromise(() => fs3.stat(entryPath).catch(() => null));
|
|
353
379
|
if (stat4?.isDirectory() && !entry.startsWith(".")) {
|
|
354
380
|
const subagentsPath = path2.join(entryPath, "subagents");
|
|
355
|
-
const subagentsExists = yield*
|
|
356
|
-
() =>
|
|
381
|
+
const subagentsExists = yield* Effect2.tryPromise(
|
|
382
|
+
() => fs3.stat(subagentsPath).then(() => true).catch(() => false)
|
|
357
383
|
);
|
|
358
384
|
if (subagentsExists) {
|
|
359
|
-
const subagentFiles = yield*
|
|
360
|
-
() =>
|
|
385
|
+
const subagentFiles = yield* Effect2.tryPromise(
|
|
386
|
+
() => fs3.readdir(subagentsPath).catch(() => [])
|
|
361
387
|
);
|
|
362
388
|
for (const subagentFile of subagentFiles) {
|
|
363
389
|
if (subagentFile.startsWith("agent-") && subagentFile.endsWith(".jsonl")) {
|
|
364
390
|
const filePath = path2.join(subagentsPath, subagentFile);
|
|
365
|
-
const orphan = yield*
|
|
391
|
+
const orphan = yield* Effect2.tryPromise(() => checkAgentFile(filePath));
|
|
366
392
|
if (orphan) {
|
|
367
393
|
orphanAgents.push({ ...orphan, filePath });
|
|
368
394
|
}
|
|
@@ -373,11 +399,11 @@ var findOrphanAgentsWithPaths = (projectName) => Effect.gen(function* () {
|
|
|
373
399
|
}
|
|
374
400
|
return orphanAgents;
|
|
375
401
|
});
|
|
376
|
-
var findOrphanAgents = (projectName) =>
|
|
402
|
+
var findOrphanAgents = (projectName) => Effect2.gen(function* () {
|
|
377
403
|
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
378
404
|
return orphans.map(({ agentId, sessionId }) => ({ agentId, sessionId }));
|
|
379
405
|
});
|
|
380
|
-
var deleteOrphanAgents = (projectName) =>
|
|
406
|
+
var deleteOrphanAgents = (projectName) => Effect2.gen(function* () {
|
|
381
407
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
382
408
|
const orphans = yield* findOrphanAgentsWithPaths(projectName);
|
|
383
409
|
const deletedAgents = [];
|
|
@@ -391,35 +417,35 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
391
417
|
foldersToCheck.add(parentDir);
|
|
392
418
|
}
|
|
393
419
|
if (orphan.lineCount <= 2) {
|
|
394
|
-
yield*
|
|
420
|
+
yield* Effect2.tryPromise(() => fs3.unlink(orphan.filePath));
|
|
395
421
|
deletedAgents.push(orphan.agentId);
|
|
396
422
|
} else {
|
|
397
423
|
if (!backupDirCreated) {
|
|
398
424
|
const backupDir2 = path2.join(projectPath, ".bak");
|
|
399
|
-
yield*
|
|
425
|
+
yield* Effect2.tryPromise(() => fs3.mkdir(backupDir2, { recursive: true }));
|
|
400
426
|
backupDirCreated = true;
|
|
401
427
|
}
|
|
402
428
|
const backupDir = path2.join(projectPath, ".bak");
|
|
403
429
|
const agentBackupPath = path2.join(backupDir, `${orphan.agentId}.jsonl`);
|
|
404
|
-
yield*
|
|
430
|
+
yield* Effect2.tryPromise(() => fs3.rename(orphan.filePath, agentBackupPath));
|
|
405
431
|
backedUpAgents.push(orphan.agentId);
|
|
406
432
|
}
|
|
407
433
|
}
|
|
408
434
|
for (const subagentsDir of foldersToCheck) {
|
|
409
|
-
const isEmpty = yield*
|
|
410
|
-
const files = await
|
|
435
|
+
const isEmpty = yield* Effect2.tryPromise(async () => {
|
|
436
|
+
const files = await fs3.readdir(subagentsDir);
|
|
411
437
|
return files.length === 0;
|
|
412
438
|
});
|
|
413
439
|
if (isEmpty) {
|
|
414
|
-
yield*
|
|
440
|
+
yield* Effect2.tryPromise(() => fs3.rmdir(subagentsDir));
|
|
415
441
|
cleanedFolders.push(subagentsDir);
|
|
416
442
|
const sessionDir = path2.dirname(subagentsDir);
|
|
417
|
-
const sessionDirEmpty = yield*
|
|
418
|
-
const files = await
|
|
443
|
+
const sessionDirEmpty = yield* Effect2.tryPromise(async () => {
|
|
444
|
+
const files = await fs3.readdir(sessionDir);
|
|
419
445
|
return files.length === 0;
|
|
420
446
|
});
|
|
421
447
|
if (sessionDirEmpty) {
|
|
422
|
-
yield*
|
|
448
|
+
yield* Effect2.tryPromise(() => fs3.rmdir(sessionDir));
|
|
423
449
|
cleanedFolders.push(sessionDir);
|
|
424
450
|
}
|
|
425
451
|
}
|
|
@@ -435,50 +461,53 @@ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
|
|
|
435
461
|
count: deletedAgents.length + backedUpAgents.length
|
|
436
462
|
};
|
|
437
463
|
});
|
|
438
|
-
var loadAgentMessages = (projectName, _sessionId, agentId) =>
|
|
464
|
+
var loadAgentMessages = (projectName, _sessionId, agentId) => Effect2.gen(function* () {
|
|
439
465
|
const projectPath = path2.join(getSessionsDir(), projectName);
|
|
440
466
|
const agentFilePath = path2.join(projectPath, `${agentId}.jsonl`);
|
|
441
|
-
const content = yield*
|
|
467
|
+
const content = yield* Effect2.tryPromise(() => fs3.readFile(agentFilePath, "utf-8"));
|
|
442
468
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
443
469
|
const messages = [];
|
|
444
|
-
for (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
messages.push(parsed);
|
|
451
|
-
} catch {
|
|
470
|
+
for (let i = 0; i < lines.length; i++) {
|
|
471
|
+
const parsed = tryParseJsonLine(lines[i], i + 1, agentFilePath);
|
|
472
|
+
if (!parsed) continue;
|
|
473
|
+
if ("sessionId" in parsed && !("type" in parsed)) {
|
|
474
|
+
continue;
|
|
452
475
|
}
|
|
476
|
+
messages.push(parsed);
|
|
453
477
|
}
|
|
454
478
|
return messages;
|
|
455
479
|
});
|
|
456
480
|
|
|
457
481
|
// src/todos.ts
|
|
458
|
-
import { Effect as
|
|
459
|
-
import * as
|
|
482
|
+
import { Effect as Effect3 } from "effect";
|
|
483
|
+
import * as fs4 from "fs/promises";
|
|
460
484
|
import * as path3 from "path";
|
|
461
|
-
var findLinkedTodos = (sessionId, agentIds = []) =>
|
|
485
|
+
var findLinkedTodos = (sessionId, agentIds = []) => Effect3.gen(function* () {
|
|
462
486
|
const todosDir = getTodosDir();
|
|
463
|
-
const exists = yield*
|
|
464
|
-
() =>
|
|
487
|
+
const exists = yield* Effect3.tryPromise(
|
|
488
|
+
() => fs4.access(todosDir).then(() => true).catch(() => false)
|
|
465
489
|
);
|
|
466
490
|
if (!exists) {
|
|
467
|
-
return
|
|
491
|
+
return {
|
|
492
|
+
sessionId,
|
|
493
|
+
sessionTodos: [],
|
|
494
|
+
agentTodos: [],
|
|
495
|
+
hasTodos: false
|
|
496
|
+
};
|
|
468
497
|
}
|
|
469
498
|
const sessionTodoPath = path3.join(todosDir, `${sessionId}.json`);
|
|
470
499
|
let sessionTodos = [];
|
|
471
|
-
const sessionTodoExists = yield*
|
|
472
|
-
() =>
|
|
500
|
+
const sessionTodoExists = yield* Effect3.tryPromise(
|
|
501
|
+
() => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
473
502
|
);
|
|
474
503
|
if (sessionTodoExists) {
|
|
475
|
-
const content = yield*
|
|
504
|
+
const content = yield* Effect3.tryPromise(() => fs4.readFile(sessionTodoPath, "utf-8"));
|
|
476
505
|
try {
|
|
477
506
|
sessionTodos = JSON.parse(content);
|
|
478
507
|
} catch {
|
|
479
508
|
}
|
|
480
509
|
}
|
|
481
|
-
const allFiles = yield*
|
|
510
|
+
const allFiles = yield* Effect3.tryPromise(() => fs4.readdir(todosDir));
|
|
482
511
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
483
512
|
const discoveredAgentIds = new Set(agentIds);
|
|
484
513
|
for (const file of allFiles) {
|
|
@@ -491,11 +520,11 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
|
|
|
491
520
|
for (const agentId of discoveredAgentIds) {
|
|
492
521
|
const shortAgentId = agentId.replace("agent-", "");
|
|
493
522
|
const agentTodoPath = path3.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
494
|
-
const agentTodoExists = yield*
|
|
495
|
-
() =>
|
|
523
|
+
const agentTodoExists = yield* Effect3.tryPromise(
|
|
524
|
+
() => fs4.access(agentTodoPath).then(() => true).catch(() => false)
|
|
496
525
|
);
|
|
497
526
|
if (agentTodoExists) {
|
|
498
|
-
const content = yield*
|
|
527
|
+
const content = yield* Effect3.tryPromise(() => fs4.readFile(agentTodoPath, "utf-8"));
|
|
499
528
|
try {
|
|
500
529
|
const todos = JSON.parse(content);
|
|
501
530
|
if (todos.length > 0) {
|
|
@@ -513,25 +542,25 @@ var findLinkedTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
|
|
|
513
542
|
hasTodos
|
|
514
543
|
};
|
|
515
544
|
});
|
|
516
|
-
var sessionHasTodos = (sessionId, agentIds = []) =>
|
|
545
|
+
var sessionHasTodos = (sessionId, agentIds = []) => Effect3.gen(function* () {
|
|
517
546
|
const todosDir = getTodosDir();
|
|
518
|
-
const exists = yield*
|
|
519
|
-
() =>
|
|
547
|
+
const exists = yield* Effect3.tryPromise(
|
|
548
|
+
() => fs4.access(todosDir).then(() => true).catch(() => false)
|
|
520
549
|
);
|
|
521
550
|
if (!exists) return false;
|
|
522
551
|
const sessionTodoPath = path3.join(todosDir, `${sessionId}.json`);
|
|
523
|
-
const sessionTodoExists = yield*
|
|
524
|
-
() =>
|
|
552
|
+
const sessionTodoExists = yield* Effect3.tryPromise(
|
|
553
|
+
() => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
525
554
|
);
|
|
526
555
|
if (sessionTodoExists) {
|
|
527
|
-
const content = yield*
|
|
556
|
+
const content = yield* Effect3.tryPromise(() => fs4.readFile(sessionTodoPath, "utf-8"));
|
|
528
557
|
try {
|
|
529
558
|
const todos = JSON.parse(content);
|
|
530
559
|
if (todos.length > 0) return true;
|
|
531
560
|
} catch {
|
|
532
561
|
}
|
|
533
562
|
}
|
|
534
|
-
const allFiles = yield*
|
|
563
|
+
const allFiles = yield* Effect3.tryPromise(() => fs4.readdir(todosDir));
|
|
535
564
|
const agentTodoPattern = new RegExp(`^${sessionId}-agent-([a-f0-9-]+)\\.json$`);
|
|
536
565
|
const discoveredAgentIds = new Set(agentIds);
|
|
537
566
|
for (const file of allFiles) {
|
|
@@ -543,11 +572,11 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
|
|
|
543
572
|
for (const agentId of discoveredAgentIds) {
|
|
544
573
|
const shortAgentId = agentId.replace("agent-", "");
|
|
545
574
|
const agentTodoPath = path3.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
546
|
-
const agentTodoExists = yield*
|
|
547
|
-
() =>
|
|
575
|
+
const agentTodoExists = yield* Effect3.tryPromise(
|
|
576
|
+
() => fs4.access(agentTodoPath).then(() => true).catch(() => false)
|
|
548
577
|
);
|
|
549
578
|
if (agentTodoExists) {
|
|
550
|
-
const content = yield*
|
|
579
|
+
const content = yield* Effect3.tryPromise(() => fs4.readFile(agentTodoPath, "utf-8"));
|
|
551
580
|
try {
|
|
552
581
|
const todos = JSON.parse(content);
|
|
553
582
|
if (todos.length > 0) return true;
|
|
@@ -557,60 +586,60 @@ var sessionHasTodos = (sessionId, agentIds = []) => Effect2.gen(function* () {
|
|
|
557
586
|
}
|
|
558
587
|
return false;
|
|
559
588
|
});
|
|
560
|
-
var deleteLinkedTodos = (sessionId, agentIds) =>
|
|
589
|
+
var deleteLinkedTodos = (sessionId, agentIds) => Effect3.gen(function* () {
|
|
561
590
|
const todosDir = getTodosDir();
|
|
562
|
-
const exists = yield*
|
|
563
|
-
() =>
|
|
591
|
+
const exists = yield* Effect3.tryPromise(
|
|
592
|
+
() => fs4.access(todosDir).then(() => true).catch(() => false)
|
|
564
593
|
);
|
|
565
594
|
if (!exists) return { deletedCount: 0 };
|
|
566
595
|
const backupDir = path3.join(todosDir, ".bak");
|
|
567
|
-
yield*
|
|
596
|
+
yield* Effect3.tryPromise(() => fs4.mkdir(backupDir, { recursive: true }));
|
|
568
597
|
let deletedCount = 0;
|
|
569
598
|
const sessionTodoPath = path3.join(todosDir, `${sessionId}.json`);
|
|
570
|
-
const sessionTodoExists = yield*
|
|
571
|
-
() =>
|
|
599
|
+
const sessionTodoExists = yield* Effect3.tryPromise(
|
|
600
|
+
() => fs4.access(sessionTodoPath).then(() => true).catch(() => false)
|
|
572
601
|
);
|
|
573
602
|
if (sessionTodoExists) {
|
|
574
603
|
const backupPath = path3.join(backupDir, `${sessionId}.json`);
|
|
575
|
-
yield*
|
|
604
|
+
yield* Effect3.tryPromise(() => fs4.rename(sessionTodoPath, backupPath));
|
|
576
605
|
deletedCount++;
|
|
577
606
|
}
|
|
578
607
|
for (const agentId of agentIds) {
|
|
579
608
|
const shortAgentId = agentId.replace("agent-", "");
|
|
580
609
|
const agentTodoPath = path3.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
581
|
-
const agentTodoExists = yield*
|
|
582
|
-
() =>
|
|
610
|
+
const agentTodoExists = yield* Effect3.tryPromise(
|
|
611
|
+
() => fs4.access(agentTodoPath).then(() => true).catch(() => false)
|
|
583
612
|
);
|
|
584
613
|
if (agentTodoExists) {
|
|
585
614
|
const backupPath = path3.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`);
|
|
586
|
-
yield*
|
|
615
|
+
yield* Effect3.tryPromise(() => fs4.rename(agentTodoPath, backupPath));
|
|
587
616
|
deletedCount++;
|
|
588
617
|
}
|
|
589
618
|
}
|
|
590
619
|
return { deletedCount };
|
|
591
620
|
});
|
|
592
|
-
var findOrphanTodos = () =>
|
|
621
|
+
var findOrphanTodos = () => Effect3.gen(function* () {
|
|
593
622
|
const todosDir = getTodosDir();
|
|
594
623
|
const sessionsDir = getSessionsDir();
|
|
595
|
-
const [todosExists, sessionsExists] = yield*
|
|
596
|
-
|
|
597
|
-
() =>
|
|
624
|
+
const [todosExists, sessionsExists] = yield* Effect3.all([
|
|
625
|
+
Effect3.tryPromise(
|
|
626
|
+
() => fs4.access(todosDir).then(() => true).catch(() => false)
|
|
598
627
|
),
|
|
599
|
-
|
|
600
|
-
() =>
|
|
628
|
+
Effect3.tryPromise(
|
|
629
|
+
() => fs4.access(sessionsDir).then(() => true).catch(() => false)
|
|
601
630
|
)
|
|
602
631
|
]);
|
|
603
632
|
if (!todosExists || !sessionsExists) return [];
|
|
604
|
-
const todoFiles = yield*
|
|
633
|
+
const todoFiles = yield* Effect3.tryPromise(() => fs4.readdir(todosDir));
|
|
605
634
|
const jsonFiles = todoFiles.filter((f) => f.endsWith(".json"));
|
|
606
635
|
const validSessionIds = /* @__PURE__ */ new Set();
|
|
607
|
-
const projectEntries = yield*
|
|
608
|
-
() =>
|
|
636
|
+
const projectEntries = yield* Effect3.tryPromise(
|
|
637
|
+
() => fs4.readdir(sessionsDir, { withFileTypes: true })
|
|
609
638
|
);
|
|
610
639
|
for (const entry of projectEntries) {
|
|
611
640
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
612
641
|
const projectPath = path3.join(sessionsDir, entry.name);
|
|
613
|
-
const files = yield*
|
|
642
|
+
const files = yield* Effect3.tryPromise(() => fs4.readdir(projectPath));
|
|
614
643
|
for (const f of files) {
|
|
615
644
|
if (f.endsWith(".jsonl") && !f.startsWith("agent-")) {
|
|
616
645
|
validSessionIds.add(f.replace(".jsonl", ""));
|
|
@@ -629,40 +658,40 @@ var findOrphanTodos = () => Effect2.gen(function* () {
|
|
|
629
658
|
}
|
|
630
659
|
return orphans;
|
|
631
660
|
});
|
|
632
|
-
var deleteOrphanTodos = () =>
|
|
661
|
+
var deleteOrphanTodos = () => Effect3.gen(function* () {
|
|
633
662
|
const todosDir = getTodosDir();
|
|
634
663
|
const orphans = yield* findOrphanTodos();
|
|
635
664
|
if (orphans.length === 0) return { success: true, deletedCount: 0 };
|
|
636
665
|
const backupDir = path3.join(todosDir, ".bak");
|
|
637
|
-
yield*
|
|
666
|
+
yield* Effect3.tryPromise(() => fs4.mkdir(backupDir, { recursive: true }));
|
|
638
667
|
let deletedCount = 0;
|
|
639
668
|
for (const orphan of orphans) {
|
|
640
669
|
const filePath = path3.join(todosDir, orphan);
|
|
641
670
|
const backupPath = path3.join(backupDir, orphan);
|
|
642
|
-
yield*
|
|
671
|
+
yield* Effect3.tryPromise(() => fs4.rename(filePath, backupPath));
|
|
643
672
|
deletedCount++;
|
|
644
673
|
}
|
|
645
674
|
return { success: true, deletedCount };
|
|
646
675
|
});
|
|
647
676
|
|
|
648
677
|
// src/session/projects.ts
|
|
649
|
-
import { Effect as
|
|
650
|
-
import * as
|
|
678
|
+
import { Effect as Effect4 } from "effect";
|
|
679
|
+
import * as fs5 from "fs/promises";
|
|
651
680
|
import * as path4 from "path";
|
|
652
|
-
var listProjects =
|
|
681
|
+
var listProjects = Effect4.gen(function* () {
|
|
653
682
|
const sessionsDir = getSessionsDir();
|
|
654
|
-
const exists = yield*
|
|
655
|
-
() =>
|
|
683
|
+
const exists = yield* Effect4.tryPromise(
|
|
684
|
+
() => fs5.access(sessionsDir).then(() => true).catch(() => false)
|
|
656
685
|
);
|
|
657
686
|
if (!exists) {
|
|
658
687
|
return [];
|
|
659
688
|
}
|
|
660
|
-
const entries = yield*
|
|
661
|
-
const projects = yield*
|
|
689
|
+
const entries = yield* Effect4.tryPromise(() => fs5.readdir(sessionsDir, { withFileTypes: true }));
|
|
690
|
+
const projects = yield* Effect4.all(
|
|
662
691
|
entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map(
|
|
663
|
-
(entry) =>
|
|
692
|
+
(entry) => Effect4.gen(function* () {
|
|
664
693
|
const projectPath = path4.join(sessionsDir, entry.name);
|
|
665
|
-
const files = yield*
|
|
694
|
+
const files = yield* Effect4.tryPromise(() => fs5.readdir(projectPath));
|
|
666
695
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
667
696
|
return {
|
|
668
697
|
name: entry.name,
|
|
@@ -678,8 +707,8 @@ var listProjects = Effect3.gen(function* () {
|
|
|
678
707
|
});
|
|
679
708
|
|
|
680
709
|
// src/session/crud.ts
|
|
681
|
-
import { Effect as
|
|
682
|
-
import * as
|
|
710
|
+
import { Effect as Effect5, pipe, Array as A, Option as O } from "effect";
|
|
711
|
+
import * as fs6 from "fs/promises";
|
|
683
712
|
import * as path5 from "path";
|
|
684
713
|
import * as crypto from "crypto";
|
|
685
714
|
|
|
@@ -853,11 +882,9 @@ function deleteMessageWithChainRepair(messages, targetId, targetType) {
|
|
|
853
882
|
}
|
|
854
883
|
|
|
855
884
|
// src/session/crud.ts
|
|
856
|
-
var updateSessionSummary = (projectName, sessionId, newSummary) =>
|
|
885
|
+
var updateSessionSummary = (projectName, sessionId, newSummary) => Effect5.gen(function* () {
|
|
857
886
|
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
858
|
-
const
|
|
859
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
860
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
887
|
+
const messages = yield* readJsonlFile(filePath);
|
|
861
888
|
const summaryIdx = messages.findIndex((m) => m.type === "summary");
|
|
862
889
|
if (summaryIdx >= 0) {
|
|
863
890
|
messages[summaryIdx] = { ...messages[summaryIdx], summary: newSummary };
|
|
@@ -871,20 +898,18 @@ var updateSessionSummary = (projectName, sessionId, newSummary) => Effect4.gen(f
|
|
|
871
898
|
messages.unshift(summaryMsg);
|
|
872
899
|
}
|
|
873
900
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
874
|
-
yield*
|
|
901
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
875
902
|
return { success: true };
|
|
876
903
|
});
|
|
877
|
-
var listSessions = (projectName) =>
|
|
904
|
+
var listSessions = (projectName) => Effect5.gen(function* () {
|
|
878
905
|
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
879
|
-
const files = yield*
|
|
906
|
+
const files = yield* Effect5.tryPromise(() => fs6.readdir(projectPath));
|
|
880
907
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
881
|
-
const sessions = yield*
|
|
908
|
+
const sessions = yield* Effect5.all(
|
|
882
909
|
sessionFiles.map(
|
|
883
|
-
(file) =>
|
|
910
|
+
(file) => Effect5.gen(function* () {
|
|
884
911
|
const filePath = path5.join(projectPath, file);
|
|
885
|
-
const
|
|
886
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
887
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
912
|
+
const messages = yield* readJsonlFile(filePath);
|
|
888
913
|
const sessionId = file.replace(".jsonl", "");
|
|
889
914
|
const userAssistantMessages = messages.filter(
|
|
890
915
|
(m) => m.type === "user" || m.type === "assistant"
|
|
@@ -935,30 +960,24 @@ var listSessions = (projectName) => Effect4.gen(function* () {
|
|
|
935
960
|
return dateB - dateA;
|
|
936
961
|
});
|
|
937
962
|
});
|
|
938
|
-
var readSession = (projectName, sessionId) =>
|
|
963
|
+
var readSession = (projectName, sessionId) => Effect5.gen(function* () {
|
|
939
964
|
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
940
|
-
|
|
941
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
942
|
-
return lines.map((line) => JSON.parse(line));
|
|
965
|
+
return yield* readJsonlFile(filePath);
|
|
943
966
|
});
|
|
944
|
-
var deleteMessage = (projectName, sessionId, messageUuid, targetType) =>
|
|
967
|
+
var deleteMessage = (projectName, sessionId, messageUuid, targetType) => Effect5.gen(function* () {
|
|
945
968
|
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
946
|
-
const
|
|
947
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
948
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
969
|
+
const messages = yield* readJsonlFile(filePath);
|
|
949
970
|
const result = deleteMessageWithChainRepair(messages, messageUuid, targetType);
|
|
950
971
|
if (!result.deleted) {
|
|
951
972
|
return { success: false, error: "Message not found" };
|
|
952
973
|
}
|
|
953
974
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
954
|
-
yield*
|
|
975
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
955
976
|
return { success: true, deletedMessage: result.deleted };
|
|
956
977
|
});
|
|
957
|
-
var restoreMessage = (projectName, sessionId, message, index) =>
|
|
978
|
+
var restoreMessage = (projectName, sessionId, message, index) => Effect5.gen(function* () {
|
|
958
979
|
const filePath = path5.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
959
|
-
const
|
|
960
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
961
|
-
const messages = lines.map((line) => JSON.parse(line));
|
|
980
|
+
const messages = yield* readJsonlFile(filePath);
|
|
962
981
|
const msgUuid = message.uuid ?? message.messageId;
|
|
963
982
|
if (!msgUuid) {
|
|
964
983
|
return { success: false, error: "Message has no uuid or messageId" };
|
|
@@ -973,41 +992,41 @@ var restoreMessage = (projectName, sessionId, message, index) => Effect4.gen(fun
|
|
|
973
992
|
const insertIndex = Math.min(index, messages.length);
|
|
974
993
|
messages.splice(insertIndex, 0, message);
|
|
975
994
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
976
|
-
yield*
|
|
995
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
977
996
|
return { success: true };
|
|
978
997
|
});
|
|
979
|
-
var deleteSession = (projectName, sessionId) =>
|
|
998
|
+
var deleteSession = (projectName, sessionId) => Effect5.gen(function* () {
|
|
980
999
|
const sessionsDir = getSessionsDir();
|
|
981
1000
|
const projectPath = path5.join(sessionsDir, projectName);
|
|
982
1001
|
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
983
1002
|
const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
|
|
984
|
-
const stat4 = yield*
|
|
1003
|
+
const stat4 = yield* Effect5.tryPromise(() => fs6.stat(filePath));
|
|
985
1004
|
if (stat4.size === 0) {
|
|
986
|
-
yield*
|
|
1005
|
+
yield* Effect5.tryPromise(() => fs6.unlink(filePath));
|
|
987
1006
|
const agentBackupDir2 = path5.join(projectPath, ".bak");
|
|
988
|
-
yield*
|
|
1007
|
+
yield* Effect5.tryPromise(() => fs6.mkdir(agentBackupDir2, { recursive: true }));
|
|
989
1008
|
for (const agentId of linkedAgents) {
|
|
990
1009
|
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
991
1010
|
const agentBackupPath = path5.join(agentBackupDir2, `${agentId}.jsonl`);
|
|
992
|
-
yield*
|
|
1011
|
+
yield* Effect5.tryPromise(() => fs6.rename(agentPath, agentBackupPath).catch(() => {
|
|
993
1012
|
}));
|
|
994
1013
|
}
|
|
995
1014
|
yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
996
1015
|
return { success: true, deletedAgents: linkedAgents.length };
|
|
997
1016
|
}
|
|
998
1017
|
const backupDir = path5.join(sessionsDir, ".bak");
|
|
999
|
-
yield*
|
|
1018
|
+
yield* Effect5.tryPromise(() => fs6.mkdir(backupDir, { recursive: true }));
|
|
1000
1019
|
const agentBackupDir = path5.join(projectPath, ".bak");
|
|
1001
|
-
yield*
|
|
1020
|
+
yield* Effect5.tryPromise(() => fs6.mkdir(agentBackupDir, { recursive: true }));
|
|
1002
1021
|
for (const agentId of linkedAgents) {
|
|
1003
1022
|
const agentPath = path5.join(projectPath, `${agentId}.jsonl`);
|
|
1004
1023
|
const agentBackupPath = path5.join(agentBackupDir, `${agentId}.jsonl`);
|
|
1005
|
-
yield*
|
|
1024
|
+
yield* Effect5.tryPromise(() => fs6.rename(agentPath, agentBackupPath).catch(() => {
|
|
1006
1025
|
}));
|
|
1007
1026
|
}
|
|
1008
1027
|
const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents);
|
|
1009
1028
|
const backupPath = path5.join(backupDir, `${projectName}_${sessionId}.jsonl`);
|
|
1010
|
-
yield*
|
|
1029
|
+
yield* Effect5.tryPromise(() => fs6.rename(filePath, backupPath));
|
|
1011
1030
|
return {
|
|
1012
1031
|
success: true,
|
|
1013
1032
|
backupPath,
|
|
@@ -1015,15 +1034,15 @@ var deleteSession = (projectName, sessionId) => Effect4.gen(function* () {
|
|
|
1015
1034
|
deletedTodos: todosResult.deletedCount
|
|
1016
1035
|
};
|
|
1017
1036
|
});
|
|
1018
|
-
var renameSession = (projectName, sessionId, newTitle) =>
|
|
1037
|
+
var renameSession = (projectName, sessionId, newTitle) => Effect5.gen(function* () {
|
|
1019
1038
|
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1020
1039
|
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
1021
|
-
const content = yield*
|
|
1040
|
+
const content = yield* Effect5.tryPromise(() => fs6.readFile(filePath, "utf-8"));
|
|
1022
1041
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1023
1042
|
if (lines.length === 0) {
|
|
1024
1043
|
return { success: false, error: "Empty session" };
|
|
1025
1044
|
}
|
|
1026
|
-
const messages = lines
|
|
1045
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1027
1046
|
const sessionUuids = /* @__PURE__ */ new Set();
|
|
1028
1047
|
for (const msg of messages) {
|
|
1029
1048
|
if (msg.uuid && typeof msg.uuid === "string") {
|
|
@@ -1042,16 +1061,14 @@ var renameSession = (projectName, sessionId, newTitle) => Effect4.gen(function*
|
|
|
1042
1061
|
messages.unshift(customTitleRecord);
|
|
1043
1062
|
}
|
|
1044
1063
|
const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1045
|
-
yield*
|
|
1046
|
-
const projectFiles = yield*
|
|
1064
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, newContent, "utf-8"));
|
|
1065
|
+
const projectFiles = yield* Effect5.tryPromise(() => fs6.readdir(projectPath));
|
|
1047
1066
|
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1048
1067
|
const summariesTargetingThis = [];
|
|
1049
1068
|
for (const file of allJsonlFiles) {
|
|
1050
1069
|
const otherFilePath = path5.join(projectPath, file);
|
|
1051
1070
|
try {
|
|
1052
|
-
const
|
|
1053
|
-
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
1054
|
-
const otherMessages = otherLines.map((l) => JSON.parse(l));
|
|
1071
|
+
const otherMessages = yield* readJsonlFile(otherFilePath);
|
|
1055
1072
|
for (let i = 0; i < otherMessages.length; i++) {
|
|
1056
1073
|
const msg = otherMessages[i];
|
|
1057
1074
|
if (msg.type === "summary" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
@@ -1070,19 +1087,15 @@ var renameSession = (projectName, sessionId, newTitle) => Effect4.gen(function*
|
|
|
1070
1087
|
summariesTargetingThis.sort((a, b) => (a.timestamp ?? "").localeCompare(b.timestamp ?? ""));
|
|
1071
1088
|
const firstSummary = summariesTargetingThis[0];
|
|
1072
1089
|
const summaryFilePath = path5.join(projectPath, firstSummary.file);
|
|
1073
|
-
const
|
|
1074
|
-
const summaryLines = summaryContent.trim().split("\n").filter(Boolean);
|
|
1075
|
-
const summaryMessages = summaryLines.map((l) => JSON.parse(l));
|
|
1090
|
+
const summaryMessages = yield* readJsonlFile(summaryFilePath);
|
|
1076
1091
|
summaryMessages[firstSummary.idx] = {
|
|
1077
1092
|
...summaryMessages[firstSummary.idx],
|
|
1078
1093
|
summary: newTitle
|
|
1079
1094
|
};
|
|
1080
1095
|
const newSummaryContent = summaryMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1081
|
-
yield*
|
|
1096
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(summaryFilePath, newSummaryContent, "utf-8"));
|
|
1082
1097
|
} else {
|
|
1083
|
-
const
|
|
1084
|
-
const currentLines = currentContent.trim().split("\n").filter(Boolean);
|
|
1085
|
-
const currentMessages = currentLines.map((l) => JSON.parse(l));
|
|
1098
|
+
const currentMessages = yield* readJsonlFile(filePath);
|
|
1086
1099
|
const firstUserIdx = currentMessages.findIndex((m) => m.type === "user");
|
|
1087
1100
|
if (firstUserIdx >= 0) {
|
|
1088
1101
|
const firstMsg = currentMessages[firstUserIdx];
|
|
@@ -1099,52 +1112,50 @@ var renameSession = (projectName, sessionId, newTitle) => Effect4.gen(function*
|
|
|
1099
1112
|
|
|
1100
1113
|
${cleanedText}`;
|
|
1101
1114
|
const updatedContent = currentMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1102
|
-
yield*
|
|
1115
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, updatedContent, "utf-8"));
|
|
1103
1116
|
}
|
|
1104
1117
|
}
|
|
1105
1118
|
}
|
|
1106
1119
|
}
|
|
1107
1120
|
return { success: true };
|
|
1108
1121
|
});
|
|
1109
|
-
var moveSession = (sourceProject, sessionId, targetProject) =>
|
|
1122
|
+
var moveSession = (sourceProject, sessionId, targetProject) => Effect5.gen(function* () {
|
|
1110
1123
|
const sessionsDir = getSessionsDir();
|
|
1111
1124
|
const sourcePath = path5.join(sessionsDir, sourceProject);
|
|
1112
1125
|
const targetPath = path5.join(sessionsDir, targetProject);
|
|
1113
1126
|
const sourceFile = path5.join(sourcePath, `${sessionId}.jsonl`);
|
|
1114
1127
|
const targetFile = path5.join(targetPath, `${sessionId}.jsonl`);
|
|
1115
|
-
const sourceExists = yield*
|
|
1116
|
-
() =>
|
|
1128
|
+
const sourceExists = yield* Effect5.tryPromise(
|
|
1129
|
+
() => fs6.access(sourceFile).then(() => true).catch(() => false)
|
|
1117
1130
|
);
|
|
1118
1131
|
if (!sourceExists) {
|
|
1119
1132
|
return { success: false, error: "Source session not found" };
|
|
1120
1133
|
}
|
|
1121
|
-
const targetExists = yield*
|
|
1122
|
-
() =>
|
|
1134
|
+
const targetExists = yield* Effect5.tryPromise(
|
|
1135
|
+
() => fs6.access(targetFile).then(() => true).catch(() => false)
|
|
1123
1136
|
);
|
|
1124
1137
|
if (targetExists) {
|
|
1125
1138
|
return { success: false, error: "Session already exists in target project" };
|
|
1126
1139
|
}
|
|
1127
|
-
yield*
|
|
1140
|
+
yield* Effect5.tryPromise(() => fs6.mkdir(targetPath, { recursive: true }));
|
|
1128
1141
|
const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId);
|
|
1129
|
-
yield*
|
|
1142
|
+
yield* Effect5.tryPromise(() => fs6.rename(sourceFile, targetFile));
|
|
1130
1143
|
for (const agentId of linkedAgents) {
|
|
1131
1144
|
const sourceAgentFile = path5.join(sourcePath, `${agentId}.jsonl`);
|
|
1132
1145
|
const targetAgentFile = path5.join(targetPath, `${agentId}.jsonl`);
|
|
1133
|
-
const agentExists = yield*
|
|
1134
|
-
() =>
|
|
1146
|
+
const agentExists = yield* Effect5.tryPromise(
|
|
1147
|
+
() => fs6.access(sourceAgentFile).then(() => true).catch(() => false)
|
|
1135
1148
|
);
|
|
1136
1149
|
if (agentExists) {
|
|
1137
|
-
yield*
|
|
1150
|
+
yield* Effect5.tryPromise(() => fs6.rename(sourceAgentFile, targetAgentFile));
|
|
1138
1151
|
}
|
|
1139
1152
|
}
|
|
1140
1153
|
return { success: true };
|
|
1141
1154
|
});
|
|
1142
|
-
var splitSession = (projectName, sessionId, splitAtMessageUuid) =>
|
|
1155
|
+
var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect5.gen(function* () {
|
|
1143
1156
|
const projectPath = path5.join(getSessionsDir(), projectName);
|
|
1144
1157
|
const filePath = path5.join(projectPath, `${sessionId}.jsonl`);
|
|
1145
|
-
const
|
|
1146
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
1147
|
-
const allMessages = lines.map((line) => JSON.parse(line));
|
|
1158
|
+
const allMessages = yield* readJsonlFile(filePath);
|
|
1148
1159
|
const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid);
|
|
1149
1160
|
if (splitIndex === -1) {
|
|
1150
1161
|
return { success: false, error: "Message not found" };
|
|
@@ -1190,30 +1201,30 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect4.gen(f
|
|
|
1190
1201
|
updatedMovedMessages.unshift(clonedSummary);
|
|
1191
1202
|
}
|
|
1192
1203
|
const keptContent = keptMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1193
|
-
yield*
|
|
1204
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(filePath, keptContent, "utf-8"));
|
|
1194
1205
|
const newFilePath = path5.join(projectPath, `${newSessionId}.jsonl`);
|
|
1195
1206
|
const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1196
|
-
yield*
|
|
1197
|
-
const agentFiles = yield*
|
|
1207
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(newFilePath, newContent, "utf-8"));
|
|
1208
|
+
const agentFiles = yield* Effect5.tryPromise(() => fs6.readdir(projectPath));
|
|
1198
1209
|
const agentJsonlFiles = agentFiles.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
|
|
1199
1210
|
for (const agentFile of agentJsonlFiles) {
|
|
1200
1211
|
const agentPath = path5.join(projectPath, agentFile);
|
|
1201
|
-
const agentContent = yield*
|
|
1212
|
+
const agentContent = yield* Effect5.tryPromise(() => fs6.readFile(agentPath, "utf-8"));
|
|
1202
1213
|
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
1203
1214
|
if (agentLines.length === 0) continue;
|
|
1204
|
-
const
|
|
1215
|
+
const agentMessages = parseJsonlLines(agentLines, agentPath);
|
|
1216
|
+
const firstAgentMsg = agentMessages[0];
|
|
1205
1217
|
if (firstAgentMsg.sessionId === sessionId) {
|
|
1206
1218
|
const agentId = agentFile.replace("agent-", "").replace(".jsonl", "");
|
|
1207
1219
|
const isRelatedToMoved = movedMessages.some(
|
|
1208
1220
|
(msg) => msg.agentId === agentId
|
|
1209
1221
|
);
|
|
1210
1222
|
if (isRelatedToMoved) {
|
|
1211
|
-
const updatedAgentMessages =
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
});
|
|
1223
|
+
const updatedAgentMessages = agentMessages.map(
|
|
1224
|
+
(msg) => JSON.stringify({ ...msg, sessionId: newSessionId })
|
|
1225
|
+
);
|
|
1215
1226
|
const updatedAgentContent = updatedAgentMessages.join("\n") + "\n";
|
|
1216
|
-
yield*
|
|
1227
|
+
yield* Effect5.tryPromise(() => fs6.writeFile(agentPath, updatedAgentContent, "utf-8"));
|
|
1217
1228
|
}
|
|
1218
1229
|
}
|
|
1219
1230
|
}
|
|
@@ -1227,8 +1238,8 @@ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect4.gen(f
|
|
|
1227
1238
|
});
|
|
1228
1239
|
|
|
1229
1240
|
// src/session/tree.ts
|
|
1230
|
-
import { Effect as
|
|
1231
|
-
import * as
|
|
1241
|
+
import { Effect as Effect6 } from "effect";
|
|
1242
|
+
import * as fs7 from "fs/promises";
|
|
1232
1243
|
import * as path6 from "path";
|
|
1233
1244
|
var sortSessions = (sessions, sort) => {
|
|
1234
1245
|
return sessions.sort((a, b) => {
|
|
@@ -1268,12 +1279,12 @@ var sortSessions = (sessions, sort) => {
|
|
|
1268
1279
|
return sort.order === "desc" ? -comparison : comparison;
|
|
1269
1280
|
});
|
|
1270
1281
|
};
|
|
1271
|
-
var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSession, fileMtime) =>
|
|
1282
|
+
var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSession, fileMtime) => Effect6.gen(function* () {
|
|
1272
1283
|
const projectPath = path6.join(getSessionsDir(), projectName);
|
|
1273
1284
|
const filePath = path6.join(projectPath, `${sessionId}.jsonl`);
|
|
1274
|
-
const content = yield*
|
|
1285
|
+
const content = yield* Effect6.tryPromise(() => fs7.readFile(filePath, "utf-8"));
|
|
1275
1286
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1276
|
-
const messages = lines
|
|
1287
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1277
1288
|
let summaries;
|
|
1278
1289
|
if (summariesByTargetSession) {
|
|
1279
1290
|
summaries = [...summariesByTargetSession.get(sessionId) ?? []].sort((a, b) => {
|
|
@@ -1289,26 +1300,24 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
|
|
|
1289
1300
|
sessionUuids.add(msg.uuid);
|
|
1290
1301
|
}
|
|
1291
1302
|
}
|
|
1292
|
-
const projectFiles = yield*
|
|
1303
|
+
const projectFiles = yield* Effect6.tryPromise(() => fs7.readdir(projectPath));
|
|
1293
1304
|
const allJsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1294
1305
|
for (const file of allJsonlFiles) {
|
|
1295
1306
|
try {
|
|
1296
1307
|
const otherFilePath = path6.join(projectPath, file);
|
|
1297
|
-
const otherContent = yield*
|
|
1308
|
+
const otherContent = yield* Effect6.tryPromise(() => fs7.readFile(otherFilePath, "utf-8"));
|
|
1298
1309
|
const otherLines = otherContent.trim().split("\n").filter(Boolean);
|
|
1299
|
-
for (
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
}
|
|
1311
|
-
} catch {
|
|
1310
|
+
for (let i = 0; i < otherLines.length; i++) {
|
|
1311
|
+
const msg = tryParseJsonLine(otherLines[i], i + 1, otherFilePath);
|
|
1312
|
+
if (!msg) continue;
|
|
1313
|
+
if (msg.type === "summary" && typeof msg.summary === "string" && typeof msg.leafUuid === "string" && sessionUuids.has(msg.leafUuid)) {
|
|
1314
|
+
const targetMsg = messages.find((m) => m.uuid === msg.leafUuid);
|
|
1315
|
+
summaries.push({
|
|
1316
|
+
summary: msg.summary,
|
|
1317
|
+
leafUuid: msg.leafUuid,
|
|
1318
|
+
timestamp: targetMsg?.timestamp ?? msg.timestamp,
|
|
1319
|
+
sourceFile: file
|
|
1320
|
+
});
|
|
1312
1321
|
}
|
|
1313
1322
|
}
|
|
1314
1323
|
} catch {
|
|
@@ -1342,7 +1351,7 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
|
|
|
1342
1351
|
for (const agentId of linkedAgentIds) {
|
|
1343
1352
|
const agentPath = path6.join(projectPath, `${agentId}.jsonl`);
|
|
1344
1353
|
try {
|
|
1345
|
-
const agentContent = yield*
|
|
1354
|
+
const agentContent = yield* Effect6.tryPromise(() => fs7.readFile(agentPath, "utf-8"));
|
|
1346
1355
|
const agentLines = agentContent.trim().split("\n").filter(Boolean);
|
|
1347
1356
|
const agentMsgs = agentLines.map((l) => JSON.parse(l));
|
|
1348
1357
|
const agentUserAssistant = agentMsgs.filter(
|
|
@@ -1390,22 +1399,22 @@ var loadSessionTreeDataInternal = (projectName, sessionId, summariesByTargetSess
|
|
|
1390
1399
|
});
|
|
1391
1400
|
var loadSessionTreeData = (projectName, sessionId) => loadSessionTreeDataInternal(projectName, sessionId, void 0);
|
|
1392
1401
|
var DEFAULT_SORT = { field: "summary", order: "desc" };
|
|
1393
|
-
var loadProjectTreeData = (projectName, sortOptions) =>
|
|
1402
|
+
var loadProjectTreeData = (projectName, sortOptions) => Effect6.gen(function* () {
|
|
1394
1403
|
const project = (yield* listProjects).find((p) => p.name === projectName);
|
|
1395
1404
|
if (!project) {
|
|
1396
1405
|
return null;
|
|
1397
1406
|
}
|
|
1398
1407
|
const sort = sortOptions ?? DEFAULT_SORT;
|
|
1399
1408
|
const projectPath = path6.join(getSessionsDir(), projectName);
|
|
1400
|
-
const files = yield*
|
|
1409
|
+
const files = yield* Effect6.tryPromise(() => fs7.readdir(projectPath));
|
|
1401
1410
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1402
1411
|
const fileMtimes = /* @__PURE__ */ new Map();
|
|
1403
|
-
yield*
|
|
1412
|
+
yield* Effect6.all(
|
|
1404
1413
|
sessionFiles.map(
|
|
1405
|
-
(file) =>
|
|
1414
|
+
(file) => Effect6.gen(function* () {
|
|
1406
1415
|
const filePath = path6.join(projectPath, file);
|
|
1407
1416
|
try {
|
|
1408
|
-
const stat4 = yield*
|
|
1417
|
+
const stat4 = yield* Effect6.tryPromise(() => fs7.stat(filePath));
|
|
1409
1418
|
fileMtimes.set(file.replace(".jsonl", ""), stat4.mtimeMs);
|
|
1410
1419
|
} catch {
|
|
1411
1420
|
}
|
|
@@ -1416,38 +1425,36 @@ var loadProjectTreeData = (projectName, sortOptions) => Effect5.gen(function* ()
|
|
|
1416
1425
|
const globalUuidMap = /* @__PURE__ */ new Map();
|
|
1417
1426
|
const allSummaries = [];
|
|
1418
1427
|
const allJsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
1419
|
-
yield*
|
|
1428
|
+
yield* Effect6.all(
|
|
1420
1429
|
allJsonlFiles.map(
|
|
1421
|
-
(file) =>
|
|
1430
|
+
(file) => Effect6.gen(function* () {
|
|
1422
1431
|
const filePath = path6.join(projectPath, file);
|
|
1423
1432
|
const fileSessionId = file.replace(".jsonl", "");
|
|
1424
1433
|
try {
|
|
1425
|
-
const content = yield*
|
|
1434
|
+
const content = yield* Effect6.tryPromise(() => fs7.readFile(filePath, "utf-8"));
|
|
1426
1435
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1427
|
-
for (
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
} catch {
|
|
1436
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1437
|
+
const msg = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
1438
|
+
if (!msg) continue;
|
|
1439
|
+
if (msg.uuid && typeof msg.uuid === "string") {
|
|
1440
|
+
globalUuidMap.set(msg.uuid, {
|
|
1441
|
+
sessionId: fileSessionId,
|
|
1442
|
+
timestamp: msg.timestamp
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
if (msg.messageId && typeof msg.messageId === "string") {
|
|
1446
|
+
globalUuidMap.set(msg.messageId, {
|
|
1447
|
+
sessionId: fileSessionId,
|
|
1448
|
+
timestamp: msg.snapshot?.timestamp
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
if (msg.type === "summary" && typeof msg.summary === "string") {
|
|
1452
|
+
allSummaries.push({
|
|
1453
|
+
summary: msg.summary,
|
|
1454
|
+
leafUuid: msg.leafUuid,
|
|
1455
|
+
timestamp: msg.timestamp,
|
|
1456
|
+
sourceFile: file
|
|
1457
|
+
});
|
|
1451
1458
|
}
|
|
1452
1459
|
}
|
|
1453
1460
|
} catch {
|
|
@@ -1475,7 +1482,7 @@ var loadProjectTreeData = (projectName, sortOptions) => Effect5.gen(function* ()
|
|
|
1475
1482
|
}
|
|
1476
1483
|
}
|
|
1477
1484
|
}
|
|
1478
|
-
const sessions = yield*
|
|
1485
|
+
const sessions = yield* Effect6.all(
|
|
1479
1486
|
sessionFiles.map((file) => {
|
|
1480
1487
|
const sessionId = file.replace(".jsonl", "");
|
|
1481
1488
|
const mtime = fileMtimes.get(sessionId);
|
|
@@ -1500,10 +1507,10 @@ var loadProjectTreeData = (projectName, sortOptions) => Effect5.gen(function* ()
|
|
|
1500
1507
|
});
|
|
1501
1508
|
|
|
1502
1509
|
// src/session/analysis.ts
|
|
1503
|
-
import { Effect as
|
|
1504
|
-
import * as
|
|
1510
|
+
import { Effect as Effect7 } from "effect";
|
|
1511
|
+
import * as fs8 from "fs/promises";
|
|
1505
1512
|
import * as path7 from "path";
|
|
1506
|
-
var analyzeSession = (projectName, sessionId) =>
|
|
1513
|
+
var analyzeSession = (projectName, sessionId) => Effect7.gen(function* () {
|
|
1507
1514
|
const messages = yield* readSession(projectName, sessionId);
|
|
1508
1515
|
let userMessages = 0;
|
|
1509
1516
|
let assistantMessages = 0;
|
|
@@ -1634,13 +1641,13 @@ var analyzeSession = (projectName, sessionId) => Effect6.gen(function* () {
|
|
|
1634
1641
|
milestones
|
|
1635
1642
|
};
|
|
1636
1643
|
});
|
|
1637
|
-
var compressSession = (projectName, sessionId, options = {}) =>
|
|
1644
|
+
var compressSession = (projectName, sessionId, options = {}) => Effect7.gen(function* () {
|
|
1638
1645
|
const { keepSnapshots = "first_last", maxToolOutputLength = 5e3 } = options;
|
|
1639
1646
|
const filePath = path7.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
1640
|
-
const content = yield*
|
|
1647
|
+
const content = yield* Effect7.tryPromise(() => fs8.readFile(filePath, "utf-8"));
|
|
1641
1648
|
const originalSize = Buffer.byteLength(content, "utf-8");
|
|
1642
1649
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1643
|
-
const messages = lines
|
|
1650
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1644
1651
|
let removedSnapshots = 0;
|
|
1645
1652
|
let truncatedOutputs = 0;
|
|
1646
1653
|
const snapshotIndices = [];
|
|
@@ -1680,7 +1687,7 @@ var compressSession = (projectName, sessionId, options = {}) => Effect6.gen(func
|
|
|
1680
1687
|
}
|
|
1681
1688
|
const newContent = filteredMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
1682
1689
|
const compressedSize = Buffer.byteLength(newContent, "utf-8");
|
|
1683
|
-
yield*
|
|
1690
|
+
yield* Effect7.tryPromise(() => fs8.writeFile(filePath, newContent, "utf-8"));
|
|
1684
1691
|
return {
|
|
1685
1692
|
success: true,
|
|
1686
1693
|
originalSize,
|
|
@@ -1689,12 +1696,12 @@ var compressSession = (projectName, sessionId, options = {}) => Effect6.gen(func
|
|
|
1689
1696
|
truncatedOutputs
|
|
1690
1697
|
};
|
|
1691
1698
|
});
|
|
1692
|
-
var extractProjectKnowledge = (projectName, sessionIds) =>
|
|
1699
|
+
var extractProjectKnowledge = (projectName, sessionIds) => Effect7.gen(function* () {
|
|
1693
1700
|
const sessionsDir = getSessionsDir();
|
|
1694
1701
|
const projectDir = path7.join(sessionsDir, projectName);
|
|
1695
1702
|
let targetSessionIds = sessionIds;
|
|
1696
1703
|
if (!targetSessionIds) {
|
|
1697
|
-
const files = yield*
|
|
1704
|
+
const files = yield* Effect7.tryPromise(() => fs8.readdir(projectDir));
|
|
1698
1705
|
targetSessionIds = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).map((f) => f.replace(".jsonl", ""));
|
|
1699
1706
|
}
|
|
1700
1707
|
const fileModifyCount = /* @__PURE__ */ new Map();
|
|
@@ -1768,7 +1775,7 @@ function truncateText(text, maxLen) {
|
|
|
1768
1775
|
}
|
|
1769
1776
|
return cleaned;
|
|
1770
1777
|
}
|
|
1771
|
-
var summarizeSession = (projectName, sessionId, options = {}) =>
|
|
1778
|
+
var summarizeSession = (projectName, sessionId, options = {}) => Effect7.gen(function* () {
|
|
1772
1779
|
const { limit = 50, maxLength = 100 } = options;
|
|
1773
1780
|
const messages = yield* readSession(projectName, sessionId);
|
|
1774
1781
|
const lines = [];
|
|
@@ -1820,15 +1827,15 @@ var summarizeSession = (projectName, sessionId, options = {}) => Effect6.gen(fun
|
|
|
1820
1827
|
});
|
|
1821
1828
|
|
|
1822
1829
|
// src/session/cleanup.ts
|
|
1823
|
-
import { Effect as
|
|
1824
|
-
import * as
|
|
1830
|
+
import { Effect as Effect8 } from "effect";
|
|
1831
|
+
import * as fs9 from "fs/promises";
|
|
1825
1832
|
import * as path8 from "path";
|
|
1826
|
-
var cleanInvalidMessages = (projectName, sessionId) =>
|
|
1833
|
+
var cleanInvalidMessages = (projectName, sessionId) => Effect8.gen(function* () {
|
|
1827
1834
|
const filePath = path8.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
|
|
1828
|
-
const content = yield*
|
|
1835
|
+
const content = yield* Effect8.tryPromise(() => fs9.readFile(filePath, "utf-8"));
|
|
1829
1836
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1830
1837
|
if (lines.length === 0) return { removedCount: 0, remainingCount: 0 };
|
|
1831
|
-
const messages = lines
|
|
1838
|
+
const messages = parseJsonlLines(lines, filePath);
|
|
1832
1839
|
const invalidIndices = [];
|
|
1833
1840
|
messages.forEach((msg, idx) => {
|
|
1834
1841
|
if (isInvalidApiKeyMessage(msg)) {
|
|
@@ -1857,7 +1864,7 @@ var cleanInvalidMessages = (projectName, sessionId) => Effect7.gen(function* ()
|
|
|
1857
1864
|
lastValidUuid = msg.uuid;
|
|
1858
1865
|
}
|
|
1859
1866
|
const newContent = filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join("\n") + "\n" : "";
|
|
1860
|
-
yield*
|
|
1867
|
+
yield* Effect8.tryPromise(() => fs9.writeFile(filePath, newContent, "utf-8"));
|
|
1861
1868
|
const remainingUserAssistant = filtered.filter(
|
|
1862
1869
|
(m) => m.type === "user" || m.type === "assistant"
|
|
1863
1870
|
).length;
|
|
@@ -1865,14 +1872,14 @@ var cleanInvalidMessages = (projectName, sessionId) => Effect7.gen(function* ()
|
|
|
1865
1872
|
const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0;
|
|
1866
1873
|
return { removedCount: invalidIndices.length, remainingCount };
|
|
1867
1874
|
});
|
|
1868
|
-
var previewCleanup = (projectName) =>
|
|
1875
|
+
var previewCleanup = (projectName) => Effect8.gen(function* () {
|
|
1869
1876
|
const projects = yield* listProjects;
|
|
1870
1877
|
const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
|
|
1871
1878
|
const orphanTodos = yield* findOrphanTodos();
|
|
1872
1879
|
const orphanTodoCount = orphanTodos.length;
|
|
1873
|
-
const results = yield*
|
|
1880
|
+
const results = yield* Effect8.all(
|
|
1874
1881
|
targetProjects.map(
|
|
1875
|
-
(project) =>
|
|
1882
|
+
(project) => Effect8.gen(function* () {
|
|
1876
1883
|
const sessions = yield* listSessions(project.name);
|
|
1877
1884
|
const emptySessions = sessions.filter((s) => s.messageCount === 0);
|
|
1878
1885
|
const invalidSessions = sessions.filter(
|
|
@@ -1905,7 +1912,7 @@ var previewCleanup = (projectName) => Effect7.gen(function* () {
|
|
|
1905
1912
|
}
|
|
1906
1913
|
return results;
|
|
1907
1914
|
});
|
|
1908
|
-
var clearSessions = (options) =>
|
|
1915
|
+
var clearSessions = (options) => Effect8.gen(function* () {
|
|
1909
1916
|
const {
|
|
1910
1917
|
projectName,
|
|
1911
1918
|
clearEmpty = true,
|
|
@@ -1924,7 +1931,7 @@ var clearSessions = (options) => Effect7.gen(function* () {
|
|
|
1924
1931
|
if (clearInvalid) {
|
|
1925
1932
|
for (const project of targetProjects) {
|
|
1926
1933
|
const projectPath = path8.join(getSessionsDir(), project.name);
|
|
1927
|
-
const files = yield*
|
|
1934
|
+
const files = yield* Effect8.tryPromise(() => fs9.readdir(projectPath));
|
|
1928
1935
|
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
1929
1936
|
for (const file of sessionFiles) {
|
|
1930
1937
|
const sessionId = file.replace(".jsonl", "");
|
|
@@ -1980,72 +1987,96 @@ var clearSessions = (options) => Effect7.gen(function* () {
|
|
|
1980
1987
|
});
|
|
1981
1988
|
|
|
1982
1989
|
// src/session/search.ts
|
|
1983
|
-
import { Effect as
|
|
1984
|
-
import * as
|
|
1990
|
+
import { Effect as Effect9, pipe as pipe2 } from "effect";
|
|
1991
|
+
import * as fs10 from "fs/promises";
|
|
1985
1992
|
import * as path9 from "path";
|
|
1986
|
-
var
|
|
1993
|
+
var extractSnippet = (text, matchIndex, queryLength) => {
|
|
1994
|
+
const start = Math.max(0, matchIndex - 50);
|
|
1995
|
+
const end = Math.min(text.length, matchIndex + queryLength + 50);
|
|
1996
|
+
return (start > 0 ? "..." : "") + text.slice(start, end).trim() + (end < text.length ? "..." : "");
|
|
1997
|
+
};
|
|
1998
|
+
var findContentMatch = (lines, queryLower, filePath) => {
|
|
1999
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2000
|
+
const msg = tryParseJsonLine(lines[i], i + 1, filePath);
|
|
2001
|
+
if (!msg) continue;
|
|
2002
|
+
if (msg.type !== "user" && msg.type !== "assistant") continue;
|
|
2003
|
+
const text = extractTextContent(msg.message);
|
|
2004
|
+
const textLower = text.toLowerCase();
|
|
2005
|
+
const matchIndex = textLower.indexOf(queryLower);
|
|
2006
|
+
if (matchIndex !== -1) {
|
|
2007
|
+
return {
|
|
2008
|
+
msg,
|
|
2009
|
+
snippet: extractSnippet(text, matchIndex, queryLower.length)
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
return null;
|
|
2014
|
+
};
|
|
2015
|
+
var searchSessionContent = (projectName, sessionId, filePath, queryLower) => pipe2(
|
|
2016
|
+
Effect9.tryPromise(() => fs10.readFile(filePath, "utf-8")),
|
|
2017
|
+
Effect9.map((content) => {
|
|
2018
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
2019
|
+
const match = findContentMatch(lines, queryLower, filePath);
|
|
2020
|
+
if (!match) return null;
|
|
2021
|
+
return {
|
|
2022
|
+
sessionId,
|
|
2023
|
+
projectName,
|
|
2024
|
+
title: extractTitle(extractTextContent(match.msg.message)) || `Session ${sessionId.slice(0, 8)}`,
|
|
2025
|
+
matchType: "content",
|
|
2026
|
+
snippet: match.snippet,
|
|
2027
|
+
messageUuid: match.msg.uuid,
|
|
2028
|
+
timestamp: match.msg.timestamp
|
|
2029
|
+
};
|
|
2030
|
+
}),
|
|
2031
|
+
Effect9.catchAll(() => Effect9.succeed(null))
|
|
2032
|
+
);
|
|
2033
|
+
var searchProjectContent = (project, queryLower, alreadyFoundIds) => Effect9.gen(function* () {
|
|
2034
|
+
const projectPath = path9.join(getSessionsDir(), project.name);
|
|
2035
|
+
const files = yield* Effect9.tryPromise(() => fs10.readdir(projectPath));
|
|
2036
|
+
const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
2037
|
+
const searchEffects = sessionFiles.map((file) => ({
|
|
2038
|
+
sessionId: file.replace(".jsonl", ""),
|
|
2039
|
+
filePath: path9.join(projectPath, file)
|
|
2040
|
+
})).filter(({ sessionId }) => !alreadyFoundIds.has(`${project.name}:${sessionId}`)).map(
|
|
2041
|
+
({ sessionId, filePath }) => searchSessionContent(project.name, sessionId, filePath, queryLower)
|
|
2042
|
+
);
|
|
2043
|
+
const results = yield* Effect9.all(searchEffects, { concurrency: 10 });
|
|
2044
|
+
return results.filter((r) => r !== null);
|
|
2045
|
+
});
|
|
2046
|
+
var searchSessions = (query, options = {}) => Effect9.gen(function* () {
|
|
1987
2047
|
const { projectName, searchContent = false } = options;
|
|
1988
|
-
const results = [];
|
|
1989
2048
|
const queryLower = query.toLowerCase();
|
|
1990
2049
|
const projects = yield* listProjects;
|
|
1991
2050
|
const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2051
|
+
const titleSearchEffects = targetProjects.map(
|
|
2052
|
+
(project) => pipe2(
|
|
2053
|
+
listSessions(project.name),
|
|
2054
|
+
Effect9.map(
|
|
2055
|
+
(sessions) => sessions.filter((session) => (session.title ?? "").toLowerCase().includes(queryLower)).map(
|
|
2056
|
+
(session) => ({
|
|
2057
|
+
sessionId: session.id,
|
|
2058
|
+
projectName: project.name,
|
|
2059
|
+
title: session.title ?? "Untitled",
|
|
2060
|
+
matchType: "title",
|
|
2061
|
+
timestamp: session.updatedAt
|
|
2062
|
+
})
|
|
2063
|
+
)
|
|
2064
|
+
)
|
|
2065
|
+
)
|
|
2066
|
+
);
|
|
2067
|
+
const titleResultsNested = yield* Effect9.all(titleSearchEffects, { concurrency: 10 });
|
|
2068
|
+
const titleResults = titleResultsNested.flat();
|
|
2069
|
+
let contentResults = [];
|
|
2007
2070
|
if (searchContent) {
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
if (results.some((r) => r.sessionId === sessionId && r.projectName === project.name)) {
|
|
2015
|
-
continue;
|
|
2016
|
-
}
|
|
2017
|
-
const filePath = path9.join(projectPath, file);
|
|
2018
|
-
const content = yield* Effect8.tryPromise(() => fs9.readFile(filePath, "utf-8"));
|
|
2019
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
2020
|
-
for (const line of lines) {
|
|
2021
|
-
try {
|
|
2022
|
-
const msg = JSON.parse(line);
|
|
2023
|
-
if (msg.type !== "user" && msg.type !== "assistant") continue;
|
|
2024
|
-
const text = extractTextContent(msg.message);
|
|
2025
|
-
const textLower = text.toLowerCase();
|
|
2026
|
-
if (textLower.includes(queryLower)) {
|
|
2027
|
-
const matchIndex = textLower.indexOf(queryLower);
|
|
2028
|
-
const start = Math.max(0, matchIndex - 50);
|
|
2029
|
-
const end = Math.min(text.length, matchIndex + query.length + 50);
|
|
2030
|
-
const snippet = (start > 0 ? "..." : "") + text.slice(start, end).trim() + (end < text.length ? "..." : "");
|
|
2031
|
-
results.push({
|
|
2032
|
-
sessionId,
|
|
2033
|
-
projectName: project.name,
|
|
2034
|
-
title: extractTitle(extractTextContent(msg.message)) || `Session ${sessionId.slice(0, 8)}`,
|
|
2035
|
-
matchType: "content",
|
|
2036
|
-
snippet,
|
|
2037
|
-
messageUuid: msg.uuid,
|
|
2038
|
-
timestamp: msg.timestamp
|
|
2039
|
-
});
|
|
2040
|
-
break;
|
|
2041
|
-
}
|
|
2042
|
-
} catch {
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2071
|
+
const alreadyFoundIds = new Set(titleResults.map((r) => `${r.projectName}:${r.sessionId}`));
|
|
2072
|
+
const contentSearchEffects = targetProjects.map(
|
|
2073
|
+
(project) => searchProjectContent(project, queryLower, alreadyFoundIds)
|
|
2074
|
+
);
|
|
2075
|
+
const contentResultsNested = yield* Effect9.all(contentSearchEffects, { concurrency: 5 });
|
|
2076
|
+
contentResults = contentResultsNested.flat();
|
|
2047
2077
|
}
|
|
2048
|
-
|
|
2078
|
+
const allResults = [...titleResults, ...contentResults];
|
|
2079
|
+
return allResults.sort((a, b) => {
|
|
2049
2080
|
const dateA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
|
|
2050
2081
|
const dateB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
|
|
2051
2082
|
return dateB - dateA;
|
|
@@ -2053,8 +2084,8 @@ var searchSessions = (query, options = {}) => Effect8.gen(function* () {
|
|
|
2053
2084
|
});
|
|
2054
2085
|
|
|
2055
2086
|
// src/session/files.ts
|
|
2056
|
-
import { Effect as
|
|
2057
|
-
var getSessionFiles = (projectName, sessionId) =>
|
|
2087
|
+
import { Effect as Effect10 } from "effect";
|
|
2088
|
+
var getSessionFiles = (projectName, sessionId) => Effect10.gen(function* () {
|
|
2058
2089
|
const messages = yield* readSession(projectName, sessionId);
|
|
2059
2090
|
const fileChanges = [];
|
|
2060
2091
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
@@ -2108,13 +2139,13 @@ var getSessionFiles = (projectName, sessionId) => Effect9.gen(function* () {
|
|
|
2108
2139
|
});
|
|
2109
2140
|
|
|
2110
2141
|
// src/session/index-file.ts
|
|
2111
|
-
import { Effect as
|
|
2112
|
-
import * as
|
|
2142
|
+
import { Effect as Effect11 } from "effect";
|
|
2143
|
+
import * as fs11 from "fs/promises";
|
|
2113
2144
|
import * as path10 from "path";
|
|
2114
|
-
var loadSessionsIndex = (projectName) =>
|
|
2145
|
+
var loadSessionsIndex = (projectName) => Effect11.gen(function* () {
|
|
2115
2146
|
const indexPath = path10.join(getSessionsDir(), projectName, "sessions-index.json");
|
|
2116
2147
|
try {
|
|
2117
|
-
const content = yield*
|
|
2148
|
+
const content = yield* Effect11.tryPromise(() => fs11.readFile(indexPath, "utf-8"));
|
|
2118
2149
|
const index = JSON.parse(content);
|
|
2119
2150
|
return index;
|
|
2120
2151
|
} catch {
|
|
@@ -2141,10 +2172,10 @@ var sortIndexEntriesByModified = (entries) => {
|
|
|
2141
2172
|
return modB - modA;
|
|
2142
2173
|
});
|
|
2143
2174
|
};
|
|
2144
|
-
var hasSessionsIndex = (projectName) =>
|
|
2175
|
+
var hasSessionsIndex = (projectName) => Effect11.gen(function* () {
|
|
2145
2176
|
const indexPath = path10.join(getSessionsDir(), projectName, "sessions-index.json");
|
|
2146
2177
|
try {
|
|
2147
|
-
yield*
|
|
2178
|
+
yield* Effect11.tryPromise(() => fs11.access(indexPath));
|
|
2148
2179
|
return true;
|
|
2149
2180
|
} catch {
|
|
2150
2181
|
return false;
|
|
@@ -2192,8 +2223,10 @@ export {
|
|
|
2192
2223
|
maskHomePath,
|
|
2193
2224
|
moveSession,
|
|
2194
2225
|
parseCommandMessage,
|
|
2226
|
+
parseJsonlLines,
|
|
2195
2227
|
pathToFolderName,
|
|
2196
2228
|
previewCleanup,
|
|
2229
|
+
readJsonlFile,
|
|
2197
2230
|
readSession,
|
|
2198
2231
|
renameSession,
|
|
2199
2232
|
restoreMessage,
|
|
@@ -2204,6 +2237,7 @@ export {
|
|
|
2204
2237
|
sortProjects,
|
|
2205
2238
|
splitSession,
|
|
2206
2239
|
summarizeSession,
|
|
2240
|
+
tryParseJsonLine,
|
|
2207
2241
|
updateSessionSummary,
|
|
2208
2242
|
validateChain,
|
|
2209
2243
|
validateToolUseResult
|