@claude-sessions/web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/build/client/_app/immutable/assets/0.DXlw3HhJ.css +1 -0
  3. package/build/client/_app/immutable/assets/0.DXlw3HhJ.css.br +0 -0
  4. package/build/client/_app/immutable/assets/0.DXlw3HhJ.css.gz +0 -0
  5. package/build/client/_app/immutable/chunks/B2IHY0Hs.js +1 -0
  6. package/build/client/_app/immutable/chunks/B2IHY0Hs.js.br +0 -0
  7. package/build/client/_app/immutable/chunks/B2IHY0Hs.js.gz +0 -0
  8. package/build/client/_app/immutable/chunks/BCJVXGHY.js +1 -0
  9. package/build/client/_app/immutable/chunks/BCJVXGHY.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/BCJVXGHY.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/CMwK7N_O.js +1 -0
  12. package/build/client/_app/immutable/chunks/CMwK7N_O.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/CMwK7N_O.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/DGEeh5xx.js +1 -0
  15. package/build/client/_app/immutable/chunks/DGEeh5xx.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/DGEeh5xx.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/F-H9hLgW.js +2 -0
  18. package/build/client/_app/immutable/chunks/F-H9hLgW.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/F-H9hLgW.js.gz +0 -0
  20. package/build/client/_app/immutable/chunks/gR9AL7GA.js +2 -0
  21. package/build/client/_app/immutable/chunks/gR9AL7GA.js.br +0 -0
  22. package/build/client/_app/immutable/chunks/gR9AL7GA.js.gz +0 -0
  23. package/build/client/_app/immutable/entry/app.DHf60dB1.js +2 -0
  24. package/build/client/_app/immutable/entry/app.DHf60dB1.js.br +0 -0
  25. package/build/client/_app/immutable/entry/app.DHf60dB1.js.gz +0 -0
  26. package/build/client/_app/immutable/entry/start.CDJKFsT_.js +1 -0
  27. package/build/client/_app/immutable/entry/start.CDJKFsT_.js.br +0 -0
  28. package/build/client/_app/immutable/entry/start.CDJKFsT_.js.gz +0 -0
  29. package/build/client/_app/immutable/nodes/0.CqGQN7Qq.js +1 -0
  30. package/build/client/_app/immutable/nodes/0.CqGQN7Qq.js.br +0 -0
  31. package/build/client/_app/immutable/nodes/0.CqGQN7Qq.js.gz +0 -0
  32. package/build/client/_app/immutable/nodes/1.DXznisUt.js +1 -0
  33. package/build/client/_app/immutable/nodes/1.DXznisUt.js.br +1 -0
  34. package/build/client/_app/immutable/nodes/1.DXznisUt.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/2.CpdATj9H.js +74 -0
  36. package/build/client/_app/immutable/nodes/2.CpdATj9H.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/2.CpdATj9H.js.gz +0 -0
  38. package/build/client/_app/version.json +1 -0
  39. package/build/client/_app/version.json.br +0 -0
  40. package/build/client/_app/version.json.gz +0 -0
  41. package/build/client/favicon.png +0 -0
  42. package/build/env.js +45 -0
  43. package/build/handler.js +1390 -0
  44. package/build/index.js +334 -0
  45. package/build/server/chunks/0-Cj_Y620H.js +17 -0
  46. package/build/server/chunks/0-Cj_Y620H.js.map +1 -0
  47. package/build/server/chunks/1-DMyb-rE0.js +9 -0
  48. package/build/server/chunks/1-DMyb-rE0.js.map +1 -0
  49. package/build/server/chunks/2-BwX_bph6.js +9 -0
  50. package/build/server/chunks/2-BwX_bph6.js.map +1 -0
  51. package/build/server/chunks/_layout.svelte-C-GMsPO4.js +33 -0
  52. package/build/server/chunks/_layout.svelte-C-GMsPO4.js.map +1 -0
  53. package/build/server/chunks/_page.svelte-DxVsvhIx.js +113 -0
  54. package/build/server/chunks/_page.svelte-DxVsvhIx.js.map +1 -0
  55. package/build/server/chunks/_server.ts-2BcgQ1bb.js +26 -0
  56. package/build/server/chunks/_server.ts-2BcgQ1bb.js.map +1 -0
  57. package/build/server/chunks/_server.ts-9Ke_ioTe.js +18 -0
  58. package/build/server/chunks/_server.ts-9Ke_ioTe.js.map +1 -0
  59. package/build/server/chunks/_server.ts-B0R_DbTz.js +18 -0
  60. package/build/server/chunks/_server.ts-B0R_DbTz.js.map +1 -0
  61. package/build/server/chunks/_server.ts-Bw_uJ6TN.js +11 -0
  62. package/build/server/chunks/_server.ts-Bw_uJ6TN.js.map +1 -0
  63. package/build/server/chunks/_server.ts-C4WRYyD7.js +19 -0
  64. package/build/server/chunks/_server.ts-C4WRYyD7.js.map +1 -0
  65. package/build/server/chunks/_server.ts-CHX8x48n.js +27 -0
  66. package/build/server/chunks/_server.ts-CHX8x48n.js.map +1 -0
  67. package/build/server/chunks/_server.ts-CK9uNfSx.js +14 -0
  68. package/build/server/chunks/_server.ts-CK9uNfSx.js.map +1 -0
  69. package/build/server/chunks/_server.ts-DMIZvW0y.js +8 -0
  70. package/build/server/chunks/_server.ts-DMIZvW0y.js.map +1 -0
  71. package/build/server/chunks/_server.ts-D_ZSNiRN.js +28 -0
  72. package/build/server/chunks/_server.ts-D_ZSNiRN.js.map +1 -0
  73. package/build/server/chunks/_server.ts-DmMLJ93T.js +18 -0
  74. package/build/server/chunks/_server.ts-DmMLJ93T.js.map +1 -0
  75. package/build/server/chunks/_server.ts-VvKyITPh.js +29 -0
  76. package/build/server/chunks/_server.ts-VvKyITPh.js.map +1 -0
  77. package/build/server/chunks/_server.ts-jzrKCWIL.js +19 -0
  78. package/build/server/chunks/_server.ts-jzrKCWIL.js.map +1 -0
  79. package/build/server/chunks/_server.ts-mE4syQ93.js +57 -0
  80. package/build/server/chunks/_server.ts-mE4syQ93.js.map +1 -0
  81. package/build/server/chunks/context-R2425nfV.js +64 -0
  82. package/build/server/chunks/context-R2425nfV.js.map +1 -0
  83. package/build/server/chunks/error.svelte-DazOwnSn.js +45 -0
  84. package/build/server/chunks/error.svelte-DazOwnSn.js.map +1 -0
  85. package/build/server/chunks/exports-BzHwARwz.js +326 -0
  86. package/build/server/chunks/exports-BzHwARwz.js.map +1 -0
  87. package/build/server/chunks/index-CoD1IJuy.js +190 -0
  88. package/build/server/chunks/index-CoD1IJuy.js.map +1 -0
  89. package/build/server/chunks/index-r8eP1RrU.js +966 -0
  90. package/build/server/chunks/index-r8eP1RrU.js.map +1 -0
  91. package/build/server/chunks/index2-B0dUKYN8.js +716 -0
  92. package/build/server/chunks/index2-B0dUKYN8.js.map +1 -0
  93. package/build/server/index.js +7938 -0
  94. package/build/server/index.js.map +1 -0
  95. package/build/server/manifest.js +137 -0
  96. package/build/server/manifest.js.map +1 -0
  97. package/build/shims.js +32 -0
  98. package/dist/cli.js +29 -0
  99. package/package.json +45 -0
@@ -0,0 +1,716 @@
1
+ import * as path4 from 'path';
2
+ import * as os from 'os';
3
+ import { Effect, pipe, Array as Array$1, Option } from 'effect';
4
+ import * as fs3 from 'fs/promises';
5
+
6
+ var getSessionsDir = () => path4.join(os.homedir(), ".claude", "projects");
7
+ var getTodosDir = () => path4.join(os.homedir(), ".claude", "todos");
8
+ var folderNameToDisplayPath = (folderName) => {
9
+ return folderName.replace(/^-/, "/").replace(/--/g, "/.").replace(/-/g, "/");
10
+ };
11
+ var extractTextContent = (message) => {
12
+ if (!message) return "";
13
+ const content = message.content;
14
+ if (!content) return "";
15
+ if (typeof content === "string") return content;
16
+ if (Array.isArray(content)) {
17
+ return content.filter((item) => typeof item === "object" && item?.type === "text").map((item) => item.text ?? "").join("");
18
+ }
19
+ return "";
20
+ };
21
+ var extractTitle = (text) => {
22
+ if (!text) return "Untitled";
23
+ let cleaned = text.replace(/<ide_[^>]*>[\s\S]*?<\/ide_[^>]*>/g, "").trim();
24
+ if (!cleaned) return "Untitled";
25
+ if (cleaned.includes("\n\n")) {
26
+ cleaned = cleaned.split("\n\n")[0];
27
+ } else if (cleaned.includes("\n")) {
28
+ cleaned = cleaned.split("\n")[0];
29
+ }
30
+ if (cleaned.length > 100) {
31
+ return cleaned.slice(0, 100) + "...";
32
+ }
33
+ return cleaned || "Untitled";
34
+ };
35
+ var isInvalidApiKeyMessage = (msg) => {
36
+ const text = extractTextContent(msg.message);
37
+ return text.includes("Invalid API key");
38
+ };
39
+ var isContinuationSummary = (msg) => {
40
+ if (msg.isCompactSummary === true) return true;
41
+ if (msg.type !== "user") return false;
42
+ const message = msg.message;
43
+ const content = message?.content ?? "";
44
+ return content.startsWith("This session is being continued from");
45
+ };
46
+ var findLinkedAgents = (projectName, sessionId) => Effect.gen(function* () {
47
+ const projectPath = path4.join(getSessionsDir(), projectName);
48
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
49
+ const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
50
+ const linkedAgents = [];
51
+ for (const agentFile of agentFiles) {
52
+ const filePath = path4.join(projectPath, agentFile);
53
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
54
+ const firstLine = content.split("\n")[0];
55
+ if (firstLine) {
56
+ try {
57
+ const parsed = JSON.parse(firstLine);
58
+ if (parsed.sessionId === sessionId) {
59
+ linkedAgents.push(agentFile.replace(".jsonl", ""));
60
+ }
61
+ } catch {
62
+ }
63
+ }
64
+ }
65
+ return linkedAgents;
66
+ });
67
+ var findOrphanAgents = (projectName) => Effect.gen(function* () {
68
+ const projectPath = path4.join(getSessionsDir(), projectName);
69
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
70
+ const sessionIds = new Set(
71
+ files.filter((f) => !f.startsWith("agent-") && f.endsWith(".jsonl")).map((f) => f.replace(".jsonl", ""))
72
+ );
73
+ const agentFiles = files.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
74
+ const orphanAgents = [];
75
+ for (const agentFile of agentFiles) {
76
+ const filePath = path4.join(projectPath, agentFile);
77
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
78
+ const firstLine = content.split("\n")[0];
79
+ if (firstLine) {
80
+ try {
81
+ const parsed = JSON.parse(firstLine);
82
+ if (parsed.sessionId && !sessionIds.has(parsed.sessionId)) {
83
+ orphanAgents.push({
84
+ agentId: agentFile.replace(".jsonl", ""),
85
+ sessionId: parsed.sessionId
86
+ });
87
+ }
88
+ } catch {
89
+ }
90
+ }
91
+ }
92
+ return orphanAgents;
93
+ });
94
+ var deleteOrphanAgents = (projectName) => Effect.gen(function* () {
95
+ const projectPath = path4.join(getSessionsDir(), projectName);
96
+ const orphans = yield* findOrphanAgents(projectName);
97
+ const backupDir = path4.join(projectPath, ".bak");
98
+ yield* Effect.tryPromise(() => fs3.mkdir(backupDir, { recursive: true }));
99
+ const deletedAgents = [];
100
+ for (const orphan of orphans) {
101
+ const agentPath = path4.join(projectPath, `${orphan.agentId}.jsonl`);
102
+ const agentBackupPath = path4.join(backupDir, `${orphan.agentId}.jsonl`);
103
+ yield* Effect.tryPromise(() => fs3.rename(agentPath, agentBackupPath));
104
+ deletedAgents.push(orphan.agentId);
105
+ }
106
+ return { success: true, deletedAgents, count: deletedAgents.length };
107
+ });
108
+ var sessionHasTodos = (sessionId, agentIds) => Effect.gen(function* () {
109
+ const todosDir = getTodosDir();
110
+ const exists = yield* Effect.tryPromise(
111
+ () => fs3.access(todosDir).then(() => true).catch(() => false)
112
+ );
113
+ if (!exists) return false;
114
+ const sessionTodoPath = path4.join(todosDir, `${sessionId}.json`);
115
+ const sessionTodoExists = yield* Effect.tryPromise(
116
+ () => fs3.access(sessionTodoPath).then(() => true).catch(() => false)
117
+ );
118
+ if (sessionTodoExists) {
119
+ const content = yield* Effect.tryPromise(() => fs3.readFile(sessionTodoPath, "utf-8"));
120
+ try {
121
+ const todos = JSON.parse(content);
122
+ if (todos.length > 0) return true;
123
+ } catch {
124
+ }
125
+ }
126
+ for (const agentId of agentIds) {
127
+ const shortAgentId = agentId.replace("agent-", "");
128
+ const agentTodoPath = path4.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
129
+ const agentTodoExists = yield* Effect.tryPromise(
130
+ () => fs3.access(agentTodoPath).then(() => true).catch(() => false)
131
+ );
132
+ if (agentTodoExists) {
133
+ const content = yield* Effect.tryPromise(() => fs3.readFile(agentTodoPath, "utf-8"));
134
+ try {
135
+ const todos = JSON.parse(content);
136
+ if (todos.length > 0) return true;
137
+ } catch {
138
+ }
139
+ }
140
+ }
141
+ return false;
142
+ });
143
+ var deleteLinkedTodos = (sessionId, agentIds) => Effect.gen(function* () {
144
+ const todosDir = getTodosDir();
145
+ const exists = yield* Effect.tryPromise(
146
+ () => fs3.access(todosDir).then(() => true).catch(() => false)
147
+ );
148
+ if (!exists) return { deletedCount: 0 };
149
+ const backupDir = path4.join(todosDir, ".bak");
150
+ yield* Effect.tryPromise(() => fs3.mkdir(backupDir, { recursive: true }));
151
+ let deletedCount = 0;
152
+ const sessionTodoPath = path4.join(todosDir, `${sessionId}.json`);
153
+ const sessionTodoExists = yield* Effect.tryPromise(
154
+ () => fs3.access(sessionTodoPath).then(() => true).catch(() => false)
155
+ );
156
+ if (sessionTodoExists) {
157
+ const backupPath = path4.join(backupDir, `${sessionId}.json`);
158
+ yield* Effect.tryPromise(() => fs3.rename(sessionTodoPath, backupPath));
159
+ deletedCount++;
160
+ }
161
+ for (const agentId of agentIds) {
162
+ const shortAgentId = agentId.replace("agent-", "");
163
+ const agentTodoPath = path4.join(todosDir, `${sessionId}-agent-${shortAgentId}.json`);
164
+ const agentTodoExists = yield* Effect.tryPromise(
165
+ () => fs3.access(agentTodoPath).then(() => true).catch(() => false)
166
+ );
167
+ if (agentTodoExists) {
168
+ const backupPath = path4.join(backupDir, `${sessionId}-agent-${shortAgentId}.json`);
169
+ yield* Effect.tryPromise(() => fs3.rename(agentTodoPath, backupPath));
170
+ deletedCount++;
171
+ }
172
+ }
173
+ return { deletedCount };
174
+ });
175
+ var findOrphanTodos = () => Effect.gen(function* () {
176
+ const todosDir = getTodosDir();
177
+ const sessionsDir = getSessionsDir();
178
+ const [todosExists, sessionsExists] = yield* Effect.all([
179
+ Effect.tryPromise(
180
+ () => fs3.access(todosDir).then(() => true).catch(() => false)
181
+ ),
182
+ Effect.tryPromise(
183
+ () => fs3.access(sessionsDir).then(() => true).catch(() => false)
184
+ )
185
+ ]);
186
+ if (!todosExists || !sessionsExists) return [];
187
+ const todoFiles = yield* Effect.tryPromise(() => fs3.readdir(todosDir));
188
+ const jsonFiles = todoFiles.filter((f) => f.endsWith(".json"));
189
+ const validSessionIds = /* @__PURE__ */ new Set();
190
+ const projectEntries = yield* Effect.tryPromise(
191
+ () => fs3.readdir(sessionsDir, { withFileTypes: true })
192
+ );
193
+ for (const entry of projectEntries) {
194
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
195
+ const projectPath = path4.join(sessionsDir, entry.name);
196
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
197
+ for (const f of files) {
198
+ if (f.endsWith(".jsonl") && !f.startsWith("agent-")) {
199
+ validSessionIds.add(f.replace(".jsonl", ""));
200
+ }
201
+ }
202
+ }
203
+ const orphans = [];
204
+ for (const todoFile of jsonFiles) {
205
+ const match = todoFile.match(/^([a-f0-9-]+)(?:-agent-[a-f0-9]+)?\.json$/);
206
+ if (match) {
207
+ const sessionId = match[1];
208
+ if (!validSessionIds.has(sessionId)) {
209
+ orphans.push(todoFile);
210
+ }
211
+ }
212
+ }
213
+ return orphans;
214
+ });
215
+ var deleteOrphanTodos = () => Effect.gen(function* () {
216
+ const todosDir = getTodosDir();
217
+ const orphans = yield* findOrphanTodos();
218
+ if (orphans.length === 0) return { success: true, deletedCount: 0 };
219
+ const backupDir = path4.join(todosDir, ".bak");
220
+ yield* Effect.tryPromise(() => fs3.mkdir(backupDir, { recursive: true }));
221
+ let deletedCount = 0;
222
+ for (const orphan of orphans) {
223
+ const filePath = path4.join(todosDir, orphan);
224
+ const backupPath = path4.join(backupDir, orphan);
225
+ yield* Effect.tryPromise(() => fs3.rename(filePath, backupPath));
226
+ deletedCount++;
227
+ }
228
+ return { success: true, deletedCount };
229
+ });
230
+ var listProjects = Effect.gen(function* () {
231
+ const sessionsDir = getSessionsDir();
232
+ const exists = yield* Effect.tryPromise(
233
+ () => fs3.access(sessionsDir).then(() => true).catch(() => false)
234
+ );
235
+ if (!exists) {
236
+ return [];
237
+ }
238
+ const entries = yield* Effect.tryPromise(() => fs3.readdir(sessionsDir, { withFileTypes: true }));
239
+ const projects = yield* Effect.all(
240
+ entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map(
241
+ (entry) => Effect.gen(function* () {
242
+ const projectPath = path4.join(sessionsDir, entry.name);
243
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
244
+ const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
245
+ return {
246
+ name: entry.name,
247
+ displayName: folderNameToDisplayPath(entry.name),
248
+ path: projectPath,
249
+ sessionCount: sessionFiles.length
250
+ };
251
+ })
252
+ ),
253
+ { concurrency: 10 }
254
+ );
255
+ return projects;
256
+ });
257
+ var listSessions = (projectName) => Effect.gen(function* () {
258
+ const projectPath = path4.join(getSessionsDir(), projectName);
259
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
260
+ const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
261
+ const sessions = yield* Effect.all(
262
+ sessionFiles.map(
263
+ (file) => Effect.gen(function* () {
264
+ const filePath = path4.join(projectPath, file);
265
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
266
+ const lines = content.trim().split("\n").filter(Boolean);
267
+ const messages = lines.map((line) => JSON.parse(line));
268
+ const sessionId = file.replace(".jsonl", "");
269
+ const userAssistantMessages = messages.filter(
270
+ (m) => m.type === "user" || m.type === "assistant"
271
+ );
272
+ const hasSummary = messages.some((m) => m.type === "summary");
273
+ const firstMessage = userAssistantMessages[0];
274
+ const lastMessage = userAssistantMessages[userAssistantMessages.length - 1];
275
+ const title = pipe(
276
+ messages,
277
+ Array$1.findFirst((m) => m.type === "user"),
278
+ Option.map((m) => {
279
+ const text = extractTextContent(m.message);
280
+ return extractTitle(text);
281
+ }),
282
+ Option.getOrElse(() => hasSummary ? "[Summary Only]" : `Session ${sessionId.slice(0, 8)}`)
283
+ );
284
+ return {
285
+ id: sessionId,
286
+ projectName,
287
+ title,
288
+ // If session has summary but no user/assistant messages, count as 1
289
+ messageCount: userAssistantMessages.length > 0 ? userAssistantMessages.length : hasSummary ? 1 : 0,
290
+ createdAt: firstMessage?.timestamp,
291
+ updatedAt: lastMessage?.timestamp
292
+ };
293
+ })
294
+ ),
295
+ { concurrency: 10 }
296
+ );
297
+ return sessions.sort((a, b) => {
298
+ const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
299
+ const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
300
+ return dateB - dateA;
301
+ });
302
+ });
303
+ var readSession = (projectName, sessionId) => Effect.gen(function* () {
304
+ const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
305
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
306
+ const lines = content.trim().split("\n").filter(Boolean);
307
+ return lines.map((line) => JSON.parse(line));
308
+ });
309
+ var deleteMessage = (projectName, sessionId, messageUuid) => Effect.gen(function* () {
310
+ const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
311
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
312
+ const lines = content.trim().split("\n").filter(Boolean);
313
+ const messages = lines.map((line) => JSON.parse(line));
314
+ const targetIndex = messages.findIndex(
315
+ (m) => m.uuid === messageUuid || m.messageId === messageUuid
316
+ );
317
+ if (targetIndex === -1) {
318
+ return { success: false, error: "Message not found" };
319
+ }
320
+ const deletedMsg = messages[targetIndex];
321
+ const deletedUuid = deletedMsg?.uuid ?? deletedMsg?.messageId;
322
+ const parentUuid = deletedMsg?.parentUuid;
323
+ for (const msg of messages) {
324
+ if (msg.parentUuid === deletedUuid) {
325
+ msg.parentUuid = parentUuid;
326
+ }
327
+ }
328
+ messages.splice(targetIndex, 1);
329
+ const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
330
+ yield* Effect.tryPromise(() => fs3.writeFile(filePath, newContent, "utf-8"));
331
+ return { success: true };
332
+ });
333
+ var deleteSession = (projectName, sessionId) => Effect.gen(function* () {
334
+ const sessionsDir = getSessionsDir();
335
+ const projectPath = path4.join(sessionsDir, projectName);
336
+ const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
337
+ const linkedAgents = yield* findLinkedAgents(projectName, sessionId);
338
+ const stat2 = yield* Effect.tryPromise(() => fs3.stat(filePath));
339
+ if (stat2.size === 0) {
340
+ yield* Effect.tryPromise(() => fs3.unlink(filePath));
341
+ const agentBackupDir2 = path4.join(projectPath, ".bak");
342
+ yield* Effect.tryPromise(() => fs3.mkdir(agentBackupDir2, { recursive: true }));
343
+ for (const agentId of linkedAgents) {
344
+ const agentPath = path4.join(projectPath, `${agentId}.jsonl`);
345
+ const agentBackupPath = path4.join(agentBackupDir2, `${agentId}.jsonl`);
346
+ yield* Effect.tryPromise(() => fs3.rename(agentPath, agentBackupPath).catch(() => {
347
+ }));
348
+ }
349
+ yield* deleteLinkedTodos(sessionId, linkedAgents);
350
+ return { success: true, deletedAgents: linkedAgents.length };
351
+ }
352
+ const backupDir = path4.join(sessionsDir, ".bak");
353
+ yield* Effect.tryPromise(() => fs3.mkdir(backupDir, { recursive: true }));
354
+ const agentBackupDir = path4.join(projectPath, ".bak");
355
+ yield* Effect.tryPromise(() => fs3.mkdir(agentBackupDir, { recursive: true }));
356
+ for (const agentId of linkedAgents) {
357
+ const agentPath = path4.join(projectPath, `${agentId}.jsonl`);
358
+ const agentBackupPath = path4.join(agentBackupDir, `${agentId}.jsonl`);
359
+ yield* Effect.tryPromise(() => fs3.rename(agentPath, agentBackupPath).catch(() => {
360
+ }));
361
+ }
362
+ const todosResult = yield* deleteLinkedTodos(sessionId, linkedAgents);
363
+ const backupPath = path4.join(backupDir, `${projectName}_${sessionId}.jsonl`);
364
+ yield* Effect.tryPromise(() => fs3.rename(filePath, backupPath));
365
+ return {
366
+ success: true,
367
+ backupPath,
368
+ deletedAgents: linkedAgents.length,
369
+ deletedTodos: todosResult.deletedCount
370
+ };
371
+ });
372
+ var renameSession = (projectName, sessionId, newTitle) => Effect.gen(function* () {
373
+ const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
374
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
375
+ const lines = content.trim().split("\n").filter(Boolean);
376
+ if (lines.length === 0) {
377
+ return { success: false, error: "Empty session" };
378
+ }
379
+ const messages = lines.map((line) => JSON.parse(line));
380
+ const firstUserIdx = messages.findIndex((m) => m.type === "user");
381
+ if (firstUserIdx === -1) {
382
+ return { success: false, error: "No user message found" };
383
+ }
384
+ const firstMsg = messages[firstUserIdx];
385
+ if (firstMsg?.message?.content && Array.isArray(firstMsg.message.content)) {
386
+ const textIdx = firstMsg.message.content.findIndex(
387
+ (item) => typeof item === "object" && item?.type === "text" && !item.text?.trim().startsWith("<ide_")
388
+ );
389
+ if (textIdx >= 0) {
390
+ const item = firstMsg.message.content[textIdx];
391
+ const oldText = item.text ?? "";
392
+ const cleanedText = oldText.replace(/^[^\n]+\n\n/, "");
393
+ item.text = `${newTitle}
394
+
395
+ ${cleanedText}`;
396
+ }
397
+ }
398
+ const newContent = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
399
+ yield* Effect.tryPromise(() => fs3.writeFile(filePath, newContent, "utf-8"));
400
+ return { success: true };
401
+ });
402
+ var getSessionFiles = (projectName, sessionId) => Effect.gen(function* () {
403
+ const messages = yield* readSession(projectName, sessionId);
404
+ const fileChanges = [];
405
+ const seenFiles = /* @__PURE__ */ new Set();
406
+ for (const msg of messages) {
407
+ if (msg.type === "file-history-snapshot") {
408
+ const snapshot = msg;
409
+ const backups = snapshot.snapshot?.trackedFileBackups;
410
+ if (backups && typeof backups === "object") {
411
+ for (const filePath of Object.keys(backups)) {
412
+ if (!seenFiles.has(filePath)) {
413
+ seenFiles.add(filePath);
414
+ fileChanges.push({
415
+ path: filePath,
416
+ action: "modified",
417
+ timestamp: snapshot.snapshot?.timestamp,
418
+ messageUuid: snapshot.messageId ?? msg.uuid
419
+ });
420
+ }
421
+ }
422
+ }
423
+ }
424
+ if (msg.type === "assistant" && msg.message?.content) {
425
+ const content = msg.message.content;
426
+ if (Array.isArray(content)) {
427
+ for (const item of content) {
428
+ if (item && typeof item === "object" && "type" in item && item.type === "tool_use") {
429
+ const toolUse = item;
430
+ if ((toolUse.name === "Write" || toolUse.name === "Edit") && toolUse.input?.file_path) {
431
+ const filePath = toolUse.input.file_path;
432
+ if (!seenFiles.has(filePath)) {
433
+ seenFiles.add(filePath);
434
+ fileChanges.push({
435
+ path: filePath,
436
+ action: toolUse.name === "Write" ? "created" : "modified",
437
+ timestamp: msg.timestamp,
438
+ messageUuid: msg.uuid
439
+ });
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
445
+ }
446
+ }
447
+ return {
448
+ sessionId,
449
+ projectName,
450
+ files: fileChanges,
451
+ totalChanges: fileChanges.length
452
+ };
453
+ });
454
+ var moveSession = (sourceProject, sessionId, targetProject) => Effect.gen(function* () {
455
+ const sessionsDir = getSessionsDir();
456
+ const sourcePath = path4.join(sessionsDir, sourceProject);
457
+ const targetPath = path4.join(sessionsDir, targetProject);
458
+ const sourceFile = path4.join(sourcePath, `${sessionId}.jsonl`);
459
+ const targetFile = path4.join(targetPath, `${sessionId}.jsonl`);
460
+ const sourceExists = yield* Effect.tryPromise(
461
+ () => fs3.access(sourceFile).then(() => true).catch(() => false)
462
+ );
463
+ if (!sourceExists) {
464
+ return { success: false, error: "Source session not found" };
465
+ }
466
+ const targetExists = yield* Effect.tryPromise(
467
+ () => fs3.access(targetFile).then(() => true).catch(() => false)
468
+ );
469
+ if (targetExists) {
470
+ return { success: false, error: "Session already exists in target project" };
471
+ }
472
+ yield* Effect.tryPromise(() => fs3.mkdir(targetPath, { recursive: true }));
473
+ const linkedAgents = yield* findLinkedAgents(sourceProject, sessionId);
474
+ yield* Effect.tryPromise(() => fs3.rename(sourceFile, targetFile));
475
+ for (const agentId of linkedAgents) {
476
+ const sourceAgentFile = path4.join(sourcePath, `${agentId}.jsonl`);
477
+ const targetAgentFile = path4.join(targetPath, `${agentId}.jsonl`);
478
+ const agentExists = yield* Effect.tryPromise(
479
+ () => fs3.access(sourceAgentFile).then(() => true).catch(() => false)
480
+ );
481
+ if (agentExists) {
482
+ yield* Effect.tryPromise(() => fs3.rename(sourceAgentFile, targetAgentFile));
483
+ }
484
+ }
485
+ return { success: true };
486
+ });
487
+ var splitSession = (projectName, sessionId, splitAtMessageUuid) => Effect.gen(function* () {
488
+ const projectPath = path4.join(getSessionsDir(), projectName);
489
+ const filePath = path4.join(projectPath, `${sessionId}.jsonl`);
490
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
491
+ const lines = content.trim().split("\n").filter(Boolean);
492
+ const allMessages = lines.map((line) => JSON.parse(line));
493
+ const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid);
494
+ if (splitIndex === -1) {
495
+ return { success: false, error: "Message not found" };
496
+ }
497
+ if (splitIndex === 0) {
498
+ return { success: false, error: "Cannot split at first message" };
499
+ }
500
+ const newSessionId = crypto.randomUUID();
501
+ const splitMessage = allMessages[splitIndex];
502
+ const shouldDuplicate = isContinuationSummary(splitMessage);
503
+ let remainingMessages;
504
+ const movedMessages = allMessages.slice(splitIndex);
505
+ if (shouldDuplicate) {
506
+ const duplicatedMessage = {
507
+ ...splitMessage,
508
+ uuid: crypto.randomUUID(),
509
+ sessionId
510
+ // Keep original session ID
511
+ };
512
+ remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage];
513
+ } else {
514
+ remainingMessages = allMessages.slice(0, splitIndex);
515
+ }
516
+ const updatedMovedMessages = movedMessages.map((msg, index) => {
517
+ const updated = { ...msg, sessionId: newSessionId };
518
+ if (index === 0) {
519
+ updated.parentUuid = null;
520
+ }
521
+ return updated;
522
+ });
523
+ const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
524
+ yield* Effect.tryPromise(() => fs3.writeFile(filePath, remainingContent, "utf-8"));
525
+ const newFilePath = path4.join(projectPath, `${newSessionId}.jsonl`);
526
+ const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
527
+ yield* Effect.tryPromise(() => fs3.writeFile(newFilePath, newContent, "utf-8"));
528
+ const agentFiles = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
529
+ const agentJsonlFiles = agentFiles.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
530
+ for (const agentFile of agentJsonlFiles) {
531
+ const agentPath = path4.join(projectPath, agentFile);
532
+ const agentContent = yield* Effect.tryPromise(() => fs3.readFile(agentPath, "utf-8"));
533
+ const agentLines = agentContent.trim().split("\n").filter(Boolean);
534
+ if (agentLines.length === 0) continue;
535
+ const firstAgentMsg = JSON.parse(agentLines[0]);
536
+ if (firstAgentMsg.sessionId === sessionId) {
537
+ const agentId = agentFile.replace("agent-", "").replace(".jsonl", "");
538
+ const isRelatedToMoved = movedMessages.some(
539
+ (msg) => msg.agentId === agentId
540
+ );
541
+ if (isRelatedToMoved) {
542
+ const updatedAgentMessages = agentLines.map((line) => {
543
+ const msg = JSON.parse(line);
544
+ return JSON.stringify({ ...msg, sessionId: newSessionId });
545
+ });
546
+ const updatedAgentContent = updatedAgentMessages.join("\n") + "\n";
547
+ yield* Effect.tryPromise(() => fs3.writeFile(agentPath, updatedAgentContent, "utf-8"));
548
+ }
549
+ }
550
+ }
551
+ return {
552
+ success: true,
553
+ newSessionId,
554
+ newSessionPath: newFilePath,
555
+ movedMessageCount: movedMessages.length,
556
+ duplicatedSummary: shouldDuplicate
557
+ };
558
+ });
559
+ var cleanInvalidMessages = (projectName, sessionId) => Effect.gen(function* () {
560
+ const filePath = path4.join(getSessionsDir(), projectName, `${sessionId}.jsonl`);
561
+ const content = yield* Effect.tryPromise(() => fs3.readFile(filePath, "utf-8"));
562
+ const lines = content.trim().split("\n").filter(Boolean);
563
+ if (lines.length === 0) return { removedCount: 0, remainingCount: 0 };
564
+ const messages = lines.map((line) => JSON.parse(line));
565
+ const invalidIndices = [];
566
+ messages.forEach((msg, idx) => {
567
+ if (isInvalidApiKeyMessage(msg)) {
568
+ invalidIndices.push(idx);
569
+ }
570
+ });
571
+ if (invalidIndices.length === 0) {
572
+ const userAssistantCount = messages.filter(
573
+ (m) => m.type === "user" || m.type === "assistant"
574
+ ).length;
575
+ const hasSummary2 = messages.some((m) => m.type === "summary");
576
+ const remainingCount2 = userAssistantCount > 0 ? userAssistantCount : hasSummary2 ? 1 : 0;
577
+ return { removedCount: 0, remainingCount: remainingCount2 };
578
+ }
579
+ const filtered = [];
580
+ let lastValidUuid = null;
581
+ for (let i = 0; i < messages.length; i++) {
582
+ if (invalidIndices.includes(i)) {
583
+ continue;
584
+ }
585
+ const msg = messages[i];
586
+ if (msg.parentUuid && invalidIndices.some((idx) => messages[idx]?.uuid === msg.parentUuid)) {
587
+ msg.parentUuid = lastValidUuid;
588
+ }
589
+ filtered.push(msg);
590
+ lastValidUuid = msg.uuid;
591
+ }
592
+ const newContent = filtered.length > 0 ? filtered.map((m) => JSON.stringify(m)).join("\n") + "\n" : "";
593
+ yield* Effect.tryPromise(() => fs3.writeFile(filePath, newContent, "utf-8"));
594
+ const remainingUserAssistant = filtered.filter(
595
+ (m) => m.type === "user" || m.type === "assistant"
596
+ ).length;
597
+ const hasSummary = filtered.some((m) => m.type === "summary");
598
+ const remainingCount = remainingUserAssistant > 0 ? remainingUserAssistant : hasSummary ? 1 : 0;
599
+ return { removedCount: invalidIndices.length, remainingCount };
600
+ });
601
+ var previewCleanup = (projectName) => Effect.gen(function* () {
602
+ const projects = yield* listProjects;
603
+ const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
604
+ const orphanTodos = yield* findOrphanTodos();
605
+ const orphanTodoCount = orphanTodos.length;
606
+ const results = yield* Effect.all(
607
+ targetProjects.map(
608
+ (project) => Effect.gen(function* () {
609
+ const sessions = yield* listSessions(project.name);
610
+ const emptySessions = sessions.filter((s) => s.messageCount === 0);
611
+ const invalidSessions = sessions.filter(
612
+ (s) => s.title?.includes("Invalid API key") || s.title?.includes("API key")
613
+ );
614
+ let emptyWithTodosCount = 0;
615
+ for (const session of emptySessions) {
616
+ const linkedAgents = yield* findLinkedAgents(project.name, session.id);
617
+ const hasTodos = yield* sessionHasTodos(session.id, linkedAgents);
618
+ if (hasTodos) {
619
+ emptyWithTodosCount++;
620
+ }
621
+ }
622
+ const orphanAgents = yield* findOrphanAgents(project.name);
623
+ return {
624
+ project: project.name,
625
+ emptySessions,
626
+ invalidSessions,
627
+ emptyWithTodosCount,
628
+ orphanAgentCount: orphanAgents.length,
629
+ orphanTodoCount: 0
630
+ // Will set for first project only
631
+ };
632
+ })
633
+ ),
634
+ { concurrency: 5 }
635
+ );
636
+ if (results.length > 0) {
637
+ results[0] = { ...results[0], orphanTodoCount };
638
+ }
639
+ return results;
640
+ });
641
+ var clearSessions = (options) => Effect.gen(function* () {
642
+ const {
643
+ projectName,
644
+ clearEmpty = true,
645
+ clearInvalid = true,
646
+ skipWithTodos = true,
647
+ clearOrphanAgents = false,
648
+ clearOrphanTodos = false
649
+ } = options;
650
+ const projects = yield* listProjects;
651
+ const targetProjects = projectName ? projects.filter((p) => p.name === projectName) : projects;
652
+ let deletedSessionCount = 0;
653
+ let removedMessageCount = 0;
654
+ let deletedOrphanAgentCount = 0;
655
+ let deletedOrphanTodoCount = 0;
656
+ const sessionsToDelete = [];
657
+ if (clearInvalid) {
658
+ for (const project of targetProjects) {
659
+ const projectPath = path4.join(getSessionsDir(), project.name);
660
+ const files = yield* Effect.tryPromise(() => fs3.readdir(projectPath));
661
+ const sessionFiles = files.filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
662
+ for (const file of sessionFiles) {
663
+ const sessionId = file.replace(".jsonl", "");
664
+ const result = yield* cleanInvalidMessages(project.name, sessionId);
665
+ removedMessageCount += result.removedCount;
666
+ if (result.remainingCount === 0) {
667
+ sessionsToDelete.push({ project: project.name, sessionId });
668
+ }
669
+ }
670
+ }
671
+ }
672
+ if (clearEmpty) {
673
+ for (const project of targetProjects) {
674
+ const sessions = yield* listSessions(project.name);
675
+ for (const session of sessions) {
676
+ if (session.messageCount === 0) {
677
+ const alreadyMarked = sessionsToDelete.some(
678
+ (s) => s.project === project.name && s.sessionId === session.id
679
+ );
680
+ if (!alreadyMarked) {
681
+ if (skipWithTodos) {
682
+ const linkedAgents = yield* findLinkedAgents(project.name, session.id);
683
+ const hasTodos = yield* sessionHasTodos(session.id, linkedAgents);
684
+ if (hasTodos) continue;
685
+ }
686
+ sessionsToDelete.push({ project: project.name, sessionId: session.id });
687
+ }
688
+ }
689
+ }
690
+ }
691
+ }
692
+ for (const { project, sessionId } of sessionsToDelete) {
693
+ yield* deleteSession(project, sessionId);
694
+ deletedSessionCount++;
695
+ }
696
+ if (clearOrphanAgents) {
697
+ for (const project of targetProjects) {
698
+ const result = yield* deleteOrphanAgents(project.name);
699
+ deletedOrphanAgentCount += result.count;
700
+ }
701
+ }
702
+ if (clearOrphanTodos) {
703
+ const result = yield* deleteOrphanTodos();
704
+ deletedOrphanTodoCount = result.deletedCount;
705
+ }
706
+ return {
707
+ success: true,
708
+ deletedCount: deletedSessionCount,
709
+ removedMessageCount,
710
+ deletedOrphanAgentCount,
711
+ deletedOrphanTodoCount
712
+ };
713
+ });
714
+
715
+ export { listSessions as a, deleteSession as b, clearSessions as c, deleteMessage as d, getSessionFiles as e, renameSession as f, getSessionsDir as g, listProjects as l, moveSession as m, previewCleanup as p, readSession as r, splitSession as s };
716
+ //# sourceMappingURL=index2-B0dUKYN8.js.map