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